示例#1
0
/*******************************************************************************
* XXX: I'm really not sure this is completely reliable for getting a relative
* XXX: path.
*******************************************************************************/
void printKextExecutable(
    OSKextRef theKext,
    PathSpec pathSpec,
    char lineEnd)
{
    CFStringRef   executablePath = NULL;  // must release
    char        * executablePathCString = NULL;  // must free

    executablePath = copyKextExecutablePath(theKext, pathSpec);
    if (!executablePath) {
        OSKextLogMemError();
        goto finish;
    }

    executablePathCString = createUTF8CStringForCFString(executablePath);
    if (!executablePathCString) {
        OSKextLogMemError();
        goto finish;
    }

    printf("%s%c", executablePathCString, lineEnd);


finish:
    SAFE_FREE(executablePathCString);
    SAFE_RELEASE(executablePath);
    return;
}
示例#2
0
void printKextInfoDictionary(
    OSKextRef theKext,
    PathSpec pathSpec,
    char lineEnd)
{
    CFStringRef   infoDictPath = NULL;  // must release
    char        * infoDictPathCString = NULL;  // must free

    infoDictPath = copyKextInfoDictionaryPath(theKext, pathSpec);
    if (!infoDictPath) {
        OSKextLogMemError();
        goto finish;
    }

    infoDictPathCString = createUTF8CStringForCFString(infoDictPath);
    if (!infoDictPathCString) {
        OSKextLogMemError();
        goto finish;
    }

    printf("%s%c", infoDictPathCString, lineEnd);


finish:
    SAFE_FREE(infoDictPathCString);
    SAFE_RELEASE(infoDictPath);
    return;
}
示例#3
0
void printKext(
    OSKextRef theKext,
    PathSpec pathSpec,
    Boolean extra_info,
    char lineEnd)
{
    CFStringRef   bundleID      = NULL;  // do NOT release
    CFStringRef   bundleVersion = NULL;  // do NOT release

    CFStringRef   kextPath      = NULL;  // must release
    char        * kext_path     = NULL;  // must free
    char        * bundle_id     = NULL;  // must free
    char        * bundle_version = NULL;  // must free

    kextPath = copyPathForKext(theKext, pathSpec);
    if (!kextPath) {
        OSKextLogMemError();
        goto finish;
    }

    kext_path = createUTF8CStringForCFString(kextPath);
    if (!kext_path) {
        OSKextLogMemError();
        goto finish;
    }

    if (extra_info) {
        bundleID = OSKextGetIdentifier(theKext);
        bundleVersion = OSKextGetValueForInfoDictionaryKey(theKext,
            kCFBundleVersionKey);

        bundle_id = createUTF8CStringForCFString(bundleID);
        bundle_version = createUTF8CStringForCFString(bundleVersion);
        if (!bundle_id || !bundle_version) {
            OSKextLogMemError();
            goto finish;
        }

        fprintf(stdout, "%s\t%s\t%s%c", kext_path, bundle_id,
            bundle_version, lineEnd);
    } else {
        fprintf(stdout, "%s%c", kext_path, lineEnd);
    }

finish:
    SAFE_RELEASE(kextPath);
    SAFE_FREE(kext_path);
    SAFE_FREE(bundle_id);
    SAFE_FREE(bundle_version);

    return;
}
示例#4
0
/*******************************************************************************
* XXX: I'm really not sure this is completely reliable for getting a relative
* XXX: path.
*******************************************************************************/
CFStringRef copyKextInfoDictionaryPath(
    OSKextRef theKext,
    PathSpec  pathSpec)
{
    CFStringRef   result      = NULL;
    CFURLRef      kextURL     = NULL;  // do not release
    CFURLRef      kextAbsURL  = NULL;  // must release
    CFBundleRef   kextBundle  = NULL;  // must release
    CFURLRef      infoDictURL = NULL;  // must release

    kextURL = OSKextGetURL(theKext);
    if (!kextURL) {
        OSKextLog(theKext,
            kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
            "Kext has no URL!");
        goto finish;
    }
    kextAbsURL = CFURLCopyAbsoluteURL(kextURL);
    if (!kextAbsURL) {
        OSKextLogMemError();
        goto finish;
    }

    kextBundle = CFBundleCreate(kCFAllocatorDefault, kextAbsURL);
    if (!kextBundle) {
        OSKextLogMemError();
        goto finish;
    }
    infoDictURL = _CFBundleCopyInfoPlistURL(kextBundle);
    if (!infoDictURL) {
        // not able to determine error here, bundle might have no plist
        // (well, we should never have gotten here if that were the case)
        result = CFStringCreateWithCString(kCFAllocatorDefault, "",
            kCFStringEncodingUTF8);
        goto finish;
    }

    result = copyAdjustedPathForURL(theKext, infoDictURL, pathSpec);

finish:
    SAFE_RELEASE(infoDictURL);
    SAFE_RELEASE(kextBundle);
    SAFE_RELEASE(kextAbsURL);
    return result;
}
ExitStatus setPrelinkedKernelArgs(
    KcgenArgs * toolArgs,
    char      * filename)
{
    ExitStatus          result          = EX_USAGE;

    if (toolArgs->prelinkedKernelPath) {
        OSKextLog(/* kext */ NULL,
            kOSKextLogWarningLevel | kOSKextLogGeneralFlag,
            "Warning - prelinked kernel already specified; using last.");
    } else {
        toolArgs->prelinkedKernelPath = malloc(PATH_MAX);
        if (!toolArgs->prelinkedKernelPath) {
            OSKextLogMemError();
            result = EX_OSERR;
            goto finish;
        }
    }

   /* If we don't have a filename we construct a default one, automatically
    * add the system extensions folders, and note that we're using default
    * info.
    */
    if (!filename) {
        OSKextLog(/* kext */ NULL,
            kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
            "Error - prelinked kernel filename required");
        goto finish;
    } else {
        size_t len = strlcpy(toolArgs->prelinkedKernelPath, filename, PATH_MAX);
        if (len >= PATH_MAX) {
            OSKextLog(/* kext */ NULL,
                kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
                "Error - prelinked kernel filename length exceeds PATH_MAX");
            goto finish;
        }
    }

    result = EX_OK;
finish:
    return result;
}
示例#6
0
/*******************************************************************************
* XXX: I'm really not sure this is completely reliable for getting a relative
* XXX: path.
*******************************************************************************/
CFStringRef copyKextExecutablePath(
    OSKextRef theKext,
    PathSpec  pathSpec)
{
    CFStringRef   result = NULL;
    CFURLRef      kextURL       = NULL;  // do not release
    CFURLRef      kextAbsURL    = NULL;  // must release
    CFURLRef      executableURL = NULL;    // must release

    kextURL = OSKextGetURL(theKext);
    if (!kextURL) {
        OSKextLog(theKext,
            kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
            "Kext has no URL!");
        goto finish;
    }
    kextAbsURL = CFURLCopyAbsoluteURL(kextURL);
    if (!kextAbsURL) {
        OSKextLogMemError();
        goto finish;
    }

    executableURL = _CFBundleCopyExecutableURLInDirectory(kextAbsURL);
    if (!executableURL) {
        // not able to determine error here, bundle might have no executable
        result = CFStringCreateWithCString(kCFAllocatorDefault, "",
            kCFStringEncodingUTF8);
        goto finish;
    }
    result = copyAdjustedPathForURL(theKext, executableURL, pathSpec);

finish:
    SAFE_RELEASE(executableURL);
    SAFE_RELEASE(kextAbsURL);
    return result;
}
CFDataRef createMkext1ForArch(const NXArchInfo * arch, CFArrayRef archiveKexts,
    boolean_t compress)
{
    CFMutableDataRef       result            = NULL;
    CFMutableDictionaryRef kextsByIdentifier = NULL;
    Mkext1Context          context;
    mkext1_header        * mkextHeader       = NULL;  // do not free
    const uint8_t        * adler_point = 0;
    CFIndex count, i;

    result = CFDataCreateMutable(kCFAllocatorDefault, /* capaacity */ 0);
    if (!result || !createCFMutableDictionary(&kextsByIdentifier)) {
        OSKextLogMemError();
        goto finish;
    }

   /* mkext1 can only contain 1 kext for a given bundle identifier, so we
    * have to pick out the most recent versions.
    */
    count = CFArrayGetCount(archiveKexts);
    for (i = 0; i < count; i++) {
        OSKextRef   theKext = (OSKextRef)CFArrayGetValueAtIndex(archiveKexts, i);
        CFStringRef bundleIdentifier = OSKextGetIdentifier(theKext);
        OSKextRef   savedKext = (OSKextRef)CFDictionaryGetValue(kextsByIdentifier,
            bundleIdentifier);
        OSKextVersion thisVersion, savedVersion;


        if (!OSKextSupportsArchitecture(theKext, arch)) {
            continue;
        }

        if (!savedKext) {
            CFDictionarySetValue(kextsByIdentifier, bundleIdentifier, theKext);
            continue;
        }
        
        thisVersion = OSKextGetVersion(theKext);
        savedVersion = OSKextGetVersion(savedKext);
        
        if (thisVersion > savedVersion) {
            CFDictionarySetValue(kextsByIdentifier, bundleIdentifier, theKext);
        }
    }

   /* Add room for the mkext header and kext descriptors.
    */
    CFDataSetLength(result, sizeof(mkext1_header) +
        CFDictionaryGetCount(kextsByIdentifier) * sizeof(mkext_kext));

    context.mkext = result;
    context.kextIndex = 0;
    context.compressOffset = (uint32_t)CFDataGetLength(result);
    context.arch = arch;
    context.fatal = false;
    context.compress = compress;
    CFDictionaryApplyFunction(kextsByIdentifier, addToMkext1, &context);
    if (context.fatal) {
        SAFE_RELEASE_NULL(result);
        goto finish;
    }

    mkextHeader = (mkext1_header *)CFDataGetBytePtr(result);
    mkextHeader->magic = OSSwapHostToBigInt32(MKEXT_MAGIC);
    mkextHeader->signature = OSSwapHostToBigInt32(MKEXT_SIGN);
    mkextHeader->version = OSSwapHostToBigInt32(0x01008000);   // 'vers' 1.0.0
    mkextHeader->numkexts =
        OSSwapHostToBigInt32((__uint32_t)CFDictionaryGetCount(kextsByIdentifier));
    mkextHeader->cputype = OSSwapHostToBigInt32(arch->cputype);
    mkextHeader->cpusubtype = OSSwapHostToBigInt32(arch->cpusubtype);
    mkextHeader->length = OSSwapHostToBigInt32((__uint32_t)CFDataGetLength(result));

    adler_point = (UInt8 *)&mkextHeader->version;
    mkextHeader->adler32 = OSSwapHostToBigInt32(local_adler32(
        (UInt8 *)&mkextHeader->version,
        (int)(CFDataGetLength(result) - (adler_point - (uint8_t *)mkextHeader))));

    OSKextLog(/* kext */ NULL, kOSKextLogProgressLevel | kOSKextLogArchiveFlag,
        "Created mkext for %s containing %lu kexts.",
        arch->name,
        CFDictionaryGetCount(kextsByIdentifier));

finish:
    SAFE_RELEASE(kextsByIdentifier);
    return result;
}
示例#8
0
ExitStatus readArgs(
    int            * argc,
    char * const  ** argv,
    KctoolArgs     * toolArgs)
{
    ExitStatus   result         = EX_USAGE;
    ExitStatus   scratchResult  = EX_USAGE;
    int          optchar        = 0;
    int          longindex      = -1;

    bzero(toolArgs, sizeof(*toolArgs));
    
    /*****
    * Process command line arguments.
    */
    while ((optchar = getopt_long_only(*argc, *argv,
        kOptChars, sOptInfo, &longindex)) != -1) {

        switch (optchar) {
  
            case kOptArch:
                toolArgs->archInfo = NXGetArchInfoFromName(optarg);
                if (!toolArgs->archInfo) {
                    OSKextLog(/* kext */ NULL,
                        kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
                        "Unknown architecture %s.", optarg);
                    goto finish;
                }
                break;
  
            case kOptHelp:
                usage(kUsageLevelFull);
                result = kKctoolExitHelp;
                goto finish;
    
            default:
                OSKextLog(/* kext */ NULL,
                    kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
                    "unrecognized option %s", (*argv)[optind-1]);
                goto finish;
                break;

        }
        
       /* Reset longindex, because getopt_long_only() is stupid and doesn't.
        */
        longindex = -1;
    }

   /* Update the argc & argv seen by main() so that boot<>root calls
    * handle remaining args.
    */
    *argc -= optind;
    *argv += optind;

    if (*argc != 4) {
        OSKextLog(/* kext */ NULL,
            kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
            "incorrect number of arguments");
        goto finish;
    }

   /*****
    * Record remaining args from the command line.
    */
    toolArgs->kernelcachePath = (*argv)[0];
    toolArgs->kextID = CFStringCreateWithCString(kCFAllocatorDefault, (*argv)[1], kCFStringEncodingUTF8);
    if (!toolArgs->kextID) {
        OSKextLogMemError();
        result = EX_OSERR;
        goto finish;
    }
    toolArgs->segmentName = (*argv)[2];
    toolArgs->sectionName = (*argv)[3];

    result = EX_OK;

finish:
    if (result == EX_USAGE) {
        usage(kUsageLevelBrief);
    }
    
    return result;
}
示例#9
0
/*******************************************************************************
* copyAdjustedPathForURL()
*
* This function takes an URL with a given kext, and adjusts it to be absolute
* or relative to the kext's containing repository, properly handling plugin
* kexts to include the repository-path of the containing kext as well.
*******************************************************************************/
CFStringRef copyAdjustedPathForURL(
    OSKextRef theKext,
    CFURLRef  urlToAdjust,
    PathSpec  pathSpec)
{
    CFStringRef result       = NULL;
    CFURLRef    absURL       = NULL;  // must release
    CFStringRef absPath      = NULL;  // must release
    CFStringRef kextAbsPath  = NULL;  // must release
    CFStringRef kextRelPath  = NULL;  // must release
    CFStringRef pathInKext   = NULL;  // must release
    CFRange     scratchRange;

    if (pathSpec != kPathsFull && pathSpec != kPathsRelative) {
        OSKextLog(theKext,
            kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
            "Invalid argument to copyAdjustedPathForURL().");
    }

    absURL = CFURLCopyAbsoluteURL(urlToAdjust);
    if (!absURL) {
        OSKextLogMemError();
        goto finish;
    }

    if (pathSpec == kPathsFull) {
        result = CFURLCopyFileSystemPath(absURL, kCFURLPOSIXPathStyle);
        goto finish;
    }

   /*****
    * Okay, we are doing repository-relative paths here. Here's how!
    * We are strip the matching part of the kext's absolute path
    * from the URL/path handed in, which gives us the path in the kext.
    * Then we tack that back onto the kext's repository-relative path. Got it?
    */

    kextAbsPath = copyPathForKext(theKext, kPathsFull);
    kextRelPath = copyPathForKext(theKext, kPathsRelative);
    absPath = CFURLCopyFileSystemPath(absURL, kCFURLPOSIXPathStyle);
    if (!kextAbsPath || !kextRelPath || !absPath) {
        goto finish;
    }

    scratchRange = CFRangeMake(CFStringGetLength(kextAbsPath),
        CFStringGetLength(absPath) - CFStringGetLength(kextAbsPath));
    pathInKext = CFStringCreateWithSubstring(kCFAllocatorDefault, absPath,
        scratchRange);
    if (!pathInKext) {
        OSKextLogMemError();
    }
    result = CFStringCreateWithFormat(kCFAllocatorDefault, /* options */ 0,
        CFSTR("%@%@"), kextRelPath, pathInKext);
    
finish:
    SAFE_RELEASE(absURL);
    SAFE_RELEASE(absPath);
    SAFE_RELEASE(kextAbsPath);
    SAFE_RELEASE(kextRelPath);
    SAFE_RELEASE(pathInKext);
    return result;
}
示例#10
0
CFStringRef copyPathForKext(
    OSKextRef theKext,
    PathSpec  pathSpec)
{
    CFStringRef result          = CFSTR("(can't determine kext path)");

    CFURLRef    kextURL         = OSKextGetURL(theKext);  // do not release
    CFURLRef    absURL          = NULL;  // must release
    OSKextRef   containerKext   = NULL;  // must release
    CFURLRef    containerURL    = NULL;  // do not release
    CFURLRef    containerAbsURL = NULL;  // must release
    CFURLRef    repositoryURL   = NULL;  // must release
    CFStringRef repositoryPath  = NULL;  // must release
    CFStringRef kextPath        = NULL;  // must release


    if (!kextURL) {
        OSKextLog(theKext,
            kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
            "Kext has no URL!");
        goto finish;
    }

    if (pathSpec == kPathsNone) {
        result = CFURLCopyLastPathComponent(kextURL);
    } else if (pathSpec == kPathsFull) {
        absURL = CFURLCopyAbsoluteURL(kextURL);
        if (!absURL) {
            OSKextLogMemError();
            goto finish;
        }
        result = CFURLCopyFileSystemPath(absURL, kCFURLPOSIXPathStyle);
    } else if (pathSpec == kPathsRelative) {
        CFRange relativeRange;
        absURL = CFURLCopyAbsoluteURL(kextURL);
        if (!absURL) {
            OSKextLogMemError();
            goto finish;
        }

        containerKext = OSKextCopyContainerForPluginKext(theKext);
        if (containerKext) {
            containerURL = OSKextGetURL(containerKext);
            if (!containerURL) {
                OSKextLog(containerKext,
                    kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
                    "Container kext has no URL!");
                goto finish;
            }
            containerAbsURL = CFURLCopyAbsoluteURL(containerURL);
            if (!containerAbsURL) {
                OSKextLogMemError();
                goto finish;
            }
            repositoryURL = CFURLCreateCopyDeletingLastPathComponent(
                kCFAllocatorDefault, containerAbsURL);
            if (!repositoryURL) {
                OSKextLogMemError();
                goto finish;
            }
        } else {
            repositoryURL = CFURLCreateCopyDeletingLastPathComponent(
                kCFAllocatorDefault, absURL);
            if (!repositoryURL) {
                OSKextLogMemError();
                goto finish;
            }
        }

        repositoryPath = CFURLCopyFileSystemPath(repositoryURL, kCFURLPOSIXPathStyle);
        kextPath = CFURLCopyFileSystemPath(absURL, kCFURLPOSIXPathStyle);
        if (!repositoryPath || !kextPath) {
            OSKextLogMemError();
            goto finish;
        }
        
       /* We add 1 to the length of the repositoryPath to handle the
        * intermediate '/' character.
        */
        relativeRange = CFRangeMake(1+CFStringGetLength(repositoryPath),
            CFStringGetLength(kextPath) - (1+CFStringGetLength(repositoryPath)));
        result = CFStringCreateWithSubstring(kCFAllocatorDefault,
            kextPath, relativeRange);
    } else {
        OSKextLog(/* kext */ NULL,
            kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
            "Internal error.");
        goto finish;
    }

finish:
    SAFE_RELEASE(absURL);
    SAFE_RELEASE(containerKext);
    SAFE_RELEASE(containerAbsURL);
    SAFE_RELEASE(repositoryURL);
    SAFE_RELEASE(repositoryPath);
    SAFE_RELEASE(kextPath);

    return result;
}
ExitStatus main(int argc, char * const * argv)
{
    ExitStatus          result        = EX_OK;
    KextstatArgs        toolArgs;
    CFDictionaryRef   * kextInfoList  = NULL;  // must free
    CFIndex             count, i;

    if (argv[0]) {
        progname = argv[0];
    }

   /* Set the OSKext log callback right away.
    */
    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 = OSKextCopyLoadedKextInfo(toolArgs.bundleIDs,
        NULL /* all info */);

    if (!toolArgs.loadedKextInfo) {
        OSKextLog(/* kext */ 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      ");
        if (toolArgs.flagShowArchitecture) {
            printf("Architecture       ");
        }
        printf("Name (Version) <Linked Against>\n");
    }

    count = CFDictionaryGetCount(toolArgs.loadedKextInfo);
    if (!count) {
        goto finish;
    }

    kextInfoList = (CFDictionaryRef *)malloc(count * sizeof(CFDictionaryRef));
    if (!kextInfoList) {
        OSKextLogMemError();
        result = EX_OSERR;
        goto finish;
    }

    CFDictionaryGetKeysAndValues(toolArgs.loadedKextInfo, /* keys */ NULL,
        (const void **)kextInfoList);
    qsort(kextInfoList, count, sizeof(CFDictionaryRef), &compareKextInfo);
    for (i = 0; i < count; i++) {
        printKextInfo(kextInfoList[i], &toolArgs);
    }

finish:
    exit(result);

    SAFE_FREE(kextInfoList);

    return result;
}
void printKextInfo(CFDictionaryRef kextInfo, KextstatArgs * toolArgs)
{
    CFBooleanRef      isKernelComponent      = NULL;  // do not release
    CFNumberRef       loadTag                = NULL;  // do not release
    CFNumberRef       retainCount            = NULL;  // do not release
    CFNumberRef       loadAddress            = NULL;  // do not release
    CFNumberRef       loadSize               = NULL;  // do not release
    CFNumberRef       wiredSize              = NULL;  // do not release
    CFStringRef       bundleID               = NULL;  // do not release
    CFStringRef       bundleVersion          = NULL;  // do not release
    CFArrayRef        dependencyLoadTags     = NULL;  // do not release
    CFMutableArrayRef sortedLoadTags         = NULL;  // must release

    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;
    uint32_t          cpuTypeValue           = (uint32_t)-1;
    uint32_t          cpuSubTypeValue        = (uint32_t)-1;
    char            * bundleIDCString        = NULL;  // must free
    char            * bundleVersionCString   = NULL;  // must free
    
    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 the -k flag was given, skip any kernel components unless
    * they are explicitly requested.
    */
    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;
    }

   /* Never print the info for the kernel (loadTag 0, id __kernel__).
    */
    if (loadTagValue == 0) {
        goto finish;
    }

    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;
    }
    if (!getNumValue(((CFNumberRef)CFDictionaryGetValue(kextInfo, CFSTR(kOSBundleCPUTypeKey))), kCFNumberSInt32Type, &cpuTypeValue)) {
        cpuTypeValue = (uint32_t)-1;
    }
    if (!getNumValue(((CFNumberRef)CFDictionaryGetValue(kextInfo, CFSTR(kOSBundleCPUSubtypeKey))), kCFNumberSInt32Type, &cpuSubTypeValue)) {
        cpuSubTypeValue = (uint32_t)-1;
    }

    bundleIDCString = createUTF8CStringForCFString(bundleID);
    bundleVersionCString = createUTF8CStringForCFString(bundleVersion);

   /* First column has no leading space.
    *
    * These field widths are from the old kextstat, may want to change them.
    */
    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);
    }

    if (toolArgs->flagShowArchitecture) {
        // include kext cputype/cpusubtype info
        if (cpuTypeValue == (uint32_t) -1) {
            fprintf(stdout, " %10s/%-7s", kStringInvalidLong, kStringInvalidLong);
        }
        else {
            const NXArchInfo * archName = NXGetArchInfoFromCpuType(cpuTypeValue, cpuSubTypeValue);

            if (archName != NULL) {
                fprintf(stdout, " %-18s", archName->name);
            }
            else {
                fprintf(stdout, " %#010x/%#-7x", cpuTypeValue, cpuSubTypeValue);
            }
        }
    }

    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, /* context */ 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;
}
ExitStatus readArgs(int argc, char * const * argv, KextstatArgs * toolArgs)
{
    ExitStatus   result        = EX_USAGE;
    CFStringRef  scratchString = NULL;  // must release
    int          optChar       = 0;
    
    bzero(toolArgs, sizeof(*toolArgs));

   /*****
    * Allocate collection objects needed for command line argument processing.
    */
    if (!createCFMutableArray(&toolArgs->bundleIDs, &kCFTypeArrayCallBacks)) {
        goto finish;
    }

   /*****
    * Process command-line arguments.
    */
    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;
            
            case kOptArchitecture:
                toolArgs->flagShowArchitecture = true;
                break;
                
            }
    }

    argc -= optind;
    argv += optind;

    if (argc) {
        OSKextLog(/* kext */ 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;
}
ExitStatus readArgs(
    int            * argc,
    char * const  ** argv,
    KcgenArgs  * toolArgs)
{
    ExitStatus   result         = EX_USAGE;
    ExitStatus   scratchResult  = EX_USAGE;
    CFStringRef  scratchString  = NULL;  // must release
    CFNumberRef  scratchNumber  = NULL;  // must release
    CFURLRef     scratchURL     = NULL;  // must release
    size_t       len            = 0;
    int32_t      i              = 0;
    int          optchar        = 0;
    int          longindex      = -1;

    bzero(toolArgs, sizeof(*toolArgs));
    
   /*****
    * Allocate collection objects.
    */
    if (!createCFMutableSet(&toolArgs->kextIDs, &kCFTypeSetCallBacks)             ||
        !createCFMutableSet(&toolArgs->optionalKextIDs, &kCFTypeSetCallBacks)     ||
        !createCFMutableArray(&toolArgs->argURLs, &kCFTypeArrayCallBacks)         ||
        !createCFMutableArray(&toolArgs->repositoryURLs, &kCFTypeArrayCallBacks)  ||
        !createCFMutableArray(&toolArgs->namedKextURLs, &kCFTypeArrayCallBacks)   ||
        !createCFMutableArray(&toolArgs->targetArchs, NULL)) {

        OSKextLogMemError();
        result = EX_OSERR;
        exit(result);
    }

    /*****
    * Process command line arguments.
    */
    while ((optchar = getopt_long_only(*argc, *argv,
        kOptChars, sOptInfo, &longindex)) != -1) {

        SAFE_RELEASE_NULL(scratchString);
        SAFE_RELEASE_NULL(scratchNumber);
        SAFE_RELEASE_NULL(scratchURL);

        /* When processing short (single-char) options, there is no way to
         * express optional arguments.  Instead, we suppress missing option
         * argument errors by adding a leading ':' to the option string.
         * When getopt detects a missing argument, it will return a ':' so that
         * we can screen for options that are not required to have an argument.
         */
        if (optchar == ':') {
            switch (optopt) {
                case kOptPrelinkedKernel:
                    optchar = optopt;
                    break;
                default:
                    OSKextLog(/* kext */ NULL,
                        kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
                        "Error - option requires an argument -- -%c.", 
                        optopt);
                    break;
            }
        }

        switch (optchar) {
  
            case kOptArch:
                if (!addArchForName(toolArgs, optarg)) {
                    OSKextLog(/* kext */ NULL,
                        kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
                        "Error - unknown architecture %s.", optarg);
                    goto finish;
                }
                break;
  
            case kOptBundleIdentifier:
                scratchString = CFStringCreateWithCString(kCFAllocatorDefault,
                   optarg, kCFStringEncodingUTF8);
                if (!scratchString) {
                    OSKextLogMemError();
                    result = EX_OSERR;
                    goto finish;
                }
                CFSetAddValue(toolArgs->kextIDs, scratchString);
                break;
  
            case kOptPrelinkedKernel:
                scratchResult = readPrelinkedKernelArgs(toolArgs, *argc, *argv,
                    /* isLongopt */ longindex != -1);
                if (scratchResult != EX_OK) {
                    result = scratchResult;
                    goto finish;
                }
                break;
  
            case kOptHelp:
                usage(kUsageLevelFull);
                result = kKcgenExitHelp;
                goto finish;
    
            case kOptKernel:
                if (toolArgs->kernelPath) {
                    OSKextLog(/* kext */ NULL,
                        kOSKextLogWarningLevel | kOSKextLogGeneralFlag,
                        "Warning - kernel file already specified; using last.");
                } else {
                    toolArgs->kernelPath = malloc(PATH_MAX);
                    if (!toolArgs->kernelPath) {
                        OSKextLogMemError();
                        result = EX_OSERR;
                        goto finish;
                    }
                }

                len = strlcpy(toolArgs->kernelPath, optarg, PATH_MAX);
                if (len >= PATH_MAX) {
                    OSKextLog(/* kext */ NULL,
                        kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
                        "Error - kernel filename length exceeds PATH_MAX");
                    goto finish;
                }
                break;
    
            case kOptTests:
                toolArgs->printTestResults = true;
                break;
  
            case kOptQuiet:
                beQuiet();
                break;

            case kOptVerbose:
                scratchResult = setLogFilterForOpt(*argc, *argv,
                    /* forceOnFlags */ kOSKextLogKextOrGlobalMask);
                if (scratchResult != EX_OK) {
                    result = scratchResult;
                    goto finish;
                }
                break;

            case kOptNoAuthentication:
                OSKextLog(/* kext */ NULL,
                    kOSKextLogWarningLevel | kOSKextLogGeneralFlag,
                    "Note: -%s is implicitly set for %s.", kOptNameNoAuthentication, progname);
                break;

            case 0:
                switch (longopt) {
                    case kLongOptOptionalBundleIdentifier:
                        scratchString = CFStringCreateWithCString(kCFAllocatorDefault,
                           optarg, kCFStringEncodingUTF8);
                        if (!scratchString) {
                            OSKextLogMemError();
                            result = EX_OSERR;
                            goto finish;
                        }
                        CFSetAddValue(toolArgs->optionalKextIDs, scratchString);
                        break;
          
                    case kLongOptCompressed:
                        toolArgs->compress = true;
                        break;

                    case kLongOptUncompressed:
                        toolArgs->uncompress = true;
                        break;

                    case kLongOptSymbols:
                        if (toolArgs->symbolDirURL) {
                            OSKextLog(/* kext */ NULL,
                                kOSKextLogWarningLevel | kOSKextLogGeneralFlag,
                                "Warning - symbol directory already specified; using last.");
                            SAFE_RELEASE_NULL(toolArgs->symbolDirURL);
                        }

                        scratchURL = CFURLCreateFromFileSystemRepresentation(
                            kCFAllocatorDefault,
                            (const UInt8 *)optarg, strlen(optarg), true);
                        if (!scratchURL) {
                            OSKextLogStringError(/* kext */ NULL);
                            result = EX_OSERR;
                            goto finish;
                        }

                        toolArgs->symbolDirURL = CFRetain(scratchURL);
                        toolArgs->generatePrelinkedSymbols = true;
                        break;

                    case kLongOptVolumeRoot:
                        if (toolArgs->volumeRootURL) {
                            OSKextLog(/* kext */ NULL,
                                kOSKextLogWarningLevel | kOSKextLogGeneralFlag,
                                "Warning: volume root already specified; using last.");
                            SAFE_RELEASE_NULL(toolArgs->volumeRootURL);
                        }

                        scratchURL = CFURLCreateFromFileSystemRepresentation(
                            kCFAllocatorDefault,
                            (const UInt8 *)optarg, strlen(optarg), true);
                        if (!scratchURL) {
                            OSKextLogStringError(/* kext */ NULL);
                            result = EX_OSERR;
                            goto finish;
                        }

                        toolArgs->volumeRootURL = CFRetain(scratchURL);
                        break;

                    case kLongOptAllPersonalities:
                        OSKextLog(/* kext */ NULL,
                            kOSKextLogWarningLevel | kOSKextLogGeneralFlag,
                            "Note: -%s is implicitly set for %s.", kOptNameAllPersonalities, progname);
                        break;

                    case kLongOptNoLinkFailures:
                        OSKextLog(/* kext */ NULL,
                            kOSKextLogWarningLevel | kOSKextLogGeneralFlag,
                            "Note: -%s is implicitly set for %s.", kOptNameNoLinkFailures, progname);
                        break;

                    case kLongOptStripSymbols:
                        toolArgs->stripSymbols = true;
                        break;

                    case kLongOptMaxSliceSize:
                        toolArgs->maxSliceSize = atol(optarg);
                        break;

                    default:
                       /* Because we use ':', getopt_long doesn't print an error message.
                        */
                        OSKextLog(/* kext */ NULL,
                            kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
                            "Error - unrecognized option %s", (*argv)[optind-1]);
                        goto finish;
                        break;
                }
                break;

            default:
               /* Because we use ':', getopt_long doesn't print an error message.
                */
                OSKextLog(/* kext */ NULL,
                    kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
                    "Error - unrecognized option %s", (*argv)[optind-1]);
                goto finish;
                break;

        }
        
       /* Reset longindex, because getopt_long_only() is stupid and doesn't.
        */
        longindex = -1;
    }

   /* Update the argc & argv seen by main().
    */
    *argc -= optind;
    *argv += optind;

   /*
    * Record the kext & directory names from the command line.
    */
    for (i = 0; i < *argc; i++) {
        SAFE_RELEASE_NULL(scratchURL);
        SAFE_RELEASE_NULL(scratchString);

        scratchURL = CFURLCreateFromFileSystemRepresentation(
            kCFAllocatorDefault,
            (const UInt8 *)(*argv)[i], strlen((*argv)[i]), true);
        if (!scratchURL) {
            OSKextLogMemError();
            result = EX_OSERR;
            goto finish;
        }
        CFArrayAppendValue(toolArgs->argURLs, scratchURL);

        scratchString = CFURLCopyPathExtension(scratchURL);
        if (scratchString && CFEqual(scratchString, CFSTR("kext"))) {
            CFArrayAppendValue(toolArgs->namedKextURLs, scratchURL);
        } else {
            CFArrayAppendValue(toolArgs->repositoryURLs, scratchURL);
        }
    }

    result = EX_OK;

finish:
    SAFE_RELEASE(scratchString);
    SAFE_RELEASE(scratchNumber);
    SAFE_RELEASE(scratchURL);

    if (result == EX_USAGE) {
        usage(kUsageLevelBrief);
    }
    return result;
}