/* * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ #include #include #include #include "kextlibs_main.h" #include "kext_tools_util.h" /******************************************************************************* * Local function prototypes and misc grotty bits. *******************************************************************************/ const char * progname = "(unknown)"; /******************************************************************************* *******************************************************************************/ int main(int argc, char * const * argv) { ExitStatus result = EX_OSERR; ExitStatus printResult = EX_OSERR; KextlibsArgs toolArgs; CFArrayRef kexts = NULL; // must release const NXArchInfo ** arches = NULL; // must free KextlibsInfo * libInfo = NULL; // must release contents & free Boolean libsAreArchSpecific = FALSE; CFIndex count, i; CFIndex numArches; /***** * Find out what the program was invoked as. */ progname = rindex(argv[0], '/'); if (progname) { progname++; // go past the '/' } else { progname = (char *)argv[0]; } /* Set the OSKext log callback right away. */ OSKextSetLogOutputFunction(&tool_log); result = readArgs(argc, argv, &toolArgs); if (result != EX_OK) { if (result == kKextlibsExitHelp) { result = EX_OK; } goto finish; } result = EX_OSERR; /***** * If necessary or requested, add the extensions folders to the * BEGINNING of the list of folders. */ count = CFArrayGetCount(toolArgs.repositoryURLs); if (!count || toolArgs.flagSysKexts) { CFArrayRef osExtFolders = OSKextGetSystemExtensionsFolderURLs(); // do not release if (!osExtFolders) { OSKextLog(/* kext */ 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(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Can't read kexts from folders."); goto finish; } toolArgs.kextURL = CFURLCreateFromFileSystemRepresentation( kCFAllocatorDefault, (u_char *)toolArgs.kextName, strlen(toolArgs.kextName), /* isDirectory */ true); if (!toolArgs.kextURL) { OSKextLogStringError(/* kext */ NULL); goto finish; } toolArgs.theKext = OSKextCreate(kCFAllocatorDefault,toolArgs. kextURL); if (!toolArgs.theKext) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Can't open %s.", toolArgs.kextName); goto finish; } arches = OSKextCopyArchitectures(toolArgs.theKext); if (!arches || !arches[0]) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Can't determine architectures of %s.", toolArgs.kextName); goto finish; } result = EX_OK; for (numArches = 0; arches[numArches]; numArches++) { /* just counting */ } libInfo = (KextlibsInfo *)malloc(numArches * sizeof(KextlibsInfo)); if (!libInfo) { OSKextLogMemError(); goto finish; } /* Find libraries for the kext for each architecture in the kext. */ 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 there's more than 1 arch, see if we have to print arch-specific * results. */ if (numArches >= 2) { for (i = 0; i < numArches - 1; i++) { if (!CFEqual(libInfo[i].libKexts, libInfo[i+1].libKexts)) { libsAreArchSpecific = TRUE; break; } } } /* If all the libs are the same for all arches, then just print them * once at the top of the output. Otherwise, only when doing XML, * print the arch-specific XML declarations before the diagnostics. */ if (!libsAreArchSpecific) { printResult = printLibs(&toolArgs, NULL, libInfo[0].libKexts, /* extraNewline? */ TRUE); /* Higher exit statuses always win. */ 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, /* extraNewline? */ i + 1 == numArches); if (printResult > result) { result = printResult; } } } } /* If all the libs are the same for all arches, then just print them * once at the top of the output. Otherwise, only when doing XML, * print the arch-specific XML declarations before the diagnostics. */ for (i = 0; i < numArches; i++) { if (libsAreArchSpecific && !toolArgs.flagXML) { printResult = printLibs(&toolArgs, arches[i], libInfo[i].libKexts, /* extraNewline? */ 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, /* printArchFlag */ !libsAreArchSpecific || toolArgs.flagXML, /* extraNewline? */ i + i < numArches); if (printResult != EX_OK) { /* Higher exit statuses always win. */ if (printResult > result) { result = printResult; } } } finish: /* We're done so we just exit without cleaning up. */ 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)); /***** * Allocate collection objects needed for command line argument processing. */ toolArgs->repositoryURLs = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); if (!toolArgs->repositoryURLs) { OSKextLogMemError(); result = EX_OSERR; goto finish; } /***** * Process command-line arguments. */ 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, /* forceOnFlags */ 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, /* directoryRequired */ TRUE, /* writableRequired */ FALSE); if (result != EX_OK) { goto finish; } toolArgs->kextName = argv[0]; argc--; argv++; if (argc) { OSKextLog(/* kext */ 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; // must release result = checkPath(path, /* suffix */ NULL, /* dirRequired? */ TRUE, /* writableRequired? */ FALSE); if (result != EX_OK) { goto finish; } url = CFURLCreateFromFileSystemRepresentation( kCFAllocatorDefault, (const UInt8 *)path, strlen(path), true); if (!url) { OSKextLog(/* kext */ 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; } /******************************************************************************* * This function prints to stdout, as it represents the only real output of * the program and we want to be able to pipe it into pbcopy. *******************************************************************************/ ExitStatus printLibs( KextlibsArgs * toolArgs, const NXArchInfo * arch, CFArrayRef libKexts, Boolean trailingNewlineFlag) { ExitStatus result = EX_OSERR; const NXArchInfo * genericArch = NULL; // do not free char * libIdentifier = NULL; // must free CFIndex count, i; if (arch) { genericArch = NXGetArchInfoFromCpuType(arch->cputype, CPU_SUBTYPE_MULTIPLE); if (!genericArch) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Can't find generic NXArchInfo for %s.", arch->name); goto finish; } } count = CFArrayGetCount(libKexts); /* For XML output, don't print anything if we found no libraries; * an empty OSBundleLibraries might look like good output to somebody. */ if (toolArgs->flagXML) { if (count) { fprintf(stdout, "\tOSBundleLibraries%s%s\n", genericArch ? "_" : "", genericArch ? genericArch->name : ""); fprintf(stdout, "\t\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%s\n", libIdentifier); fprintf(stdout, "\t\t%s\n", versCString); } else { fprintf(stdout, " %s = %s\n", libIdentifier, versCString); } } else { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Internal error generating library list."); goto finish; } } if (count && toolArgs->flagXML) { fprintf(stdout, "\t\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; // must free 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; // must free 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; // must free char * libVers = NULL; // must free CFURLRef libKextURL; char libKextName[PATH_MAX]; CFStringRef libVersString = NULL; // do not release cSymbol = createUTF8CStringForCFString((CFStringRef)key); if (!cSymbol) { OSKextLogStringError(/* kext */ NULL); goto finish; } libKextURL = OSKextGetURL(libKext); if (!CFURLGetFileSystemRepresentation(libKextURL, /* resolveToBase */ false, (u_char *)libKextName, PATH_MAX)) { OSKextLogStringError(/* kext */ NULL); goto finish; } if (flagCompatible) { libVersString = OSKextGetValueForInfoDictionaryKey(libKext, CFSTR("OSBundleCompatibleVersion")); } else { libVersString = OSKextGetValueForInfoDictionaryKey(libKext, kCFBundleVersionKey); } libVers = createUTF8CStringForCFString(libVersString); if (!libVers) { OSKextLogStringError(/* kext */ 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; // must free char * libVers = NULL; // must free 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; // do not release SAFE_FREE_NULL(libVers); libKextURL = OSKextGetURL(libKext); if (!CFURLGetFileSystemRepresentation(libKextURL, /* resolveToBase */ 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; } /******************************************************************************* * usage() *******************************************************************************/ 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"); // extra newline for spacing fprintf(stderr, ": the kext to find libraries for\n"); fprintf(stderr, "-%s :\n", kOptNameArch); fprintf(stderr, " resolve for architecture instead of running kernel's\n"); fprintf(stderr, "-%s: print XML fragment suitable for pasting\n", kOptNameXML); // fake out compiler for blank line 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 (-%c):\n", kOptNameRepository, kOptRepository); fprintf(stderr, " look in 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; }