#include <CoreFoundation/CoreFoundation.h>
#include <CoreFoundation/CFBundlePriv.h>
#include <errno.h>
#include <libc.h>
#include <libgen.h> // dirname()
#include <sys/types.h>
#include <sys/mman.h>
#include <fts.h>
#include <paths.h>
#include <mach-o/arch.h>
#include <mach-o/fat.h>
#include <mach-o/loader.h>
#include <mach-o/nlist.h>
#include <mach-o/swap.h>
#include <mach/mach.h>
#include <mach/mach_error.h>
#include <mach/mach_port.h> // mach_port_allocate()
#include <mach/mach_types.h>
#include <mach/machine/vm_param.h>
#include <mach/kmod.h>
#include <notify.h>
#include <servers/bootstrap.h> // bootstrap mach ports
#include <stdlib.h>
#include <unistd.h> // sleep(3)
#include <sys/types.h>
#include <sys/stat.h>
#include <DiskArbitration/DiskArbitrationPrivate.h>
#include <IOKit/IOTypes.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/IOKitServer.h>
#include <IOKit/IOCFUnserialize.h>
#include <IOKit/IOCFSerialize.h>
#include <libkern/OSByteOrder.h>
#include <IOKit/kext/OSKext.h>
#include <IOKit/kext/OSKextPrivate.h>
#include <IOKit/kext/kextmanager_types.h>
#include <IOKit/kext/kextmanager_mig.h>
#include <IOKit/pwr_mgt/IOPMLib.h>
#include "kcgen_main.h"
#include "compression.h"
const char * progname = "(unknown)";
int main(int argc, char * const * argv)
{
ExitStatus result = EX_SOFTWARE;
KcgenArgs toolArgs;
Boolean fatal = false;
progname = rindex(argv[0], '/');
if (progname) {
progname++; } else {
progname = (char *)argv[0];
}
OSKextSetLogOutputFunction(&tool_log);
_OSKextSetStrictRecordingByLastOpened(TRUE);
if (getenv("KEXTD_SPAWNED")) {
OSKextSetLogFilter(kDefaultServiceLogFilter | kOSKextLogKextOrGlobalMask,
false);
OSKextSetLogFilter(kDefaultServiceLogFilter | kOSKextLogKextOrGlobalMask,
true);
tool_openlog("com.apple.kcgen");
}
result = readArgs(&argc, &argv, &toolArgs);
if (result != EX_OK) {
if (result == kKcgenExitHelp) {
result = EX_OK;
}
goto finish;
}
result = checkArgs(&toolArgs);
if (result != EX_OK) {
goto finish;
}
result = EX_OK;
OSKextSetUsesCaches(false);
if (toolArgs.prelinkedKernelPath && !CFArrayGetCount(toolArgs.argURLs) &&
(toolArgs.compress || toolArgs.uncompress))
{
result = compressPrelinkedKernel(toolArgs.prelinkedKernelPath,
toolArgs.compress,
toolArgs.compressionType);
goto finish;
}
if (toolArgs.printTestResults) {
OSKextSetRecordsDiagnostics(kOSKextDiagnosticsFlagAll);
}
toolArgs.allKexts = OSKextCreateKextsFromURLs(kCFAllocatorDefault, toolArgs.argURLs);
if (!toolArgs.allKexts || !CFArrayGetCount(toolArgs.allKexts)) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Error - no kernel extensions found.");
result = EX_SOFTWARE;
goto finish;
}
toolArgs.repositoryKexts = OSKextCreateKextsFromURLs(kCFAllocatorDefault,
toolArgs.repositoryURLs);
toolArgs.namedKexts = OSKextCreateKextsFromURLs(kCFAllocatorDefault,
toolArgs.namedKextURLs);
if (!toolArgs.repositoryKexts || !toolArgs.namedKexts) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Error - failed to read extensions.");
result = EX_SOFTWARE;
goto finish;
}
if (result != EX_OK) {
goto finish;
}
if (toolArgs.prelinkedKernelPath) {
result = createPrelinkedKernel(&toolArgs);
if (result != EX_OK) {
goto finish;
}
}
finish:
exit(result);
SAFE_RELEASE(toolArgs.kextIDs);
SAFE_RELEASE(toolArgs.argURLs);
SAFE_RELEASE(toolArgs.repositoryURLs);
SAFE_RELEASE(toolArgs.namedKextURLs);
SAFE_RELEASE(toolArgs.allKexts);
SAFE_RELEASE(toolArgs.repositoryKexts);
SAFE_RELEASE(toolArgs.namedKexts);
SAFE_RELEASE(toolArgs.kernelFile);
SAFE_RELEASE(toolArgs.symbolDirURL);
SAFE_FREE(toolArgs.prelinkedKernelPath);
SAFE_FREE(toolArgs.kernelPath);
return result;
}
ExitStatus readArgs(
int * argc,
char * const ** argv,
KcgenArgs * toolArgs)
{
ExitStatus result = EX_USAGE;
ExitStatus scratchResult = EX_USAGE;
CFStringRef scratchString = NULL; CFNumberRef scratchNumber = NULL; CFURLRef scratchURL = NULL; size_t len = 0;
int32_t i = 0;
int optchar = 0;
int longindex = -1;
bzero(toolArgs, sizeof(*toolArgs));
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);
}
while ((optchar = getopt_long_only(*argc, *argv,
kOptChars, sOptInfo, &longindex)) != -1) {
SAFE_RELEASE_NULL(scratchString);
SAFE_RELEASE_NULL(scratchNumber);
SAFE_RELEASE_NULL(scratchURL);
if (optchar == ':') {
switch (optopt) {
case kOptPrelinkedKernel:
optchar = optopt;
break;
default:
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Error - option requires an argument -- -%c.",
optopt);
break;
}
}
switch (optchar) {
case kOptArch:
if (!addArchForName(toolArgs, optarg)) {
OSKextLog( 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,
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( 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( 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,
kOSKextLogKextOrGlobalMask);
if (scratchResult != EX_OK) {
result = scratchResult;
goto finish;
}
break;
case kOptNoAuthentication:
OSKextLog( 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;
if (optarg == NULL) {
toolArgs->compressionType = COMP_TYPE_LZSS;
} else if (0 == strcasecmp(optarg, "lzvn")) {
if (!supportsFastLibCompression()) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Error - lzvn compression specified but not supported");
goto finish;
}
toolArgs->compressionType = COMP_TYPE_FASTLIB;
} else if (0 == strcasecmp(optarg, "lzss")) {
toolArgs->compressionType = COMP_TYPE_LZSS;
} else {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Error - unrecognized compression type %s", optarg);
goto finish;
}
break;
case kLongOptUncompressed:
toolArgs->uncompress = true;
break;
case kLongOptSymbols:
if (toolArgs->symbolDirURL) {
OSKextLog( 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( NULL);
result = EX_OSERR;
goto finish;
}
toolArgs->symbolDirURL = CFRetain(scratchURL);
toolArgs->generatePrelinkedSymbols = true;
break;
case kLongOptVolumeRoot:
if (toolArgs->volumeRootURL) {
OSKextLog( 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( NULL);
result = EX_OSERR;
goto finish;
}
toolArgs->volumeRootURL = CFRetain(scratchURL);
break;
case kLongOptAllPersonalities:
OSKextLog( NULL,
kOSKextLogWarningLevel | kOSKextLogGeneralFlag,
"Note: -%s is implicitly set for %s.", kOptNameAllPersonalities, progname);
break;
case kLongOptNoLinkFailures:
OSKextLog( 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:
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Error - unrecognized option %s", (*argv)[optind-1]);
goto finish;
break;
}
break;
default:
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Error - unrecognized option %s", (*argv)[optind-1]);
goto finish;
break;
}
longindex = -1;
}
*argc -= optind;
*argv += optind;
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;
}
void logUsedKexts(
KcgenArgs * toolArgs,
CFArrayRef prelinkKexts)
{
int fd;
int ret;
char * kextTracePath;
char tmpBuffer[PATH_MAX + 1 + 1];
size_t tmpBufLen;
ssize_t writeSize;
CFIndex count, i;
kextTracePath = getenv("KEXT_TRACE_FILE");
if (!kextTracePath) {
return;
}
fd = open(kextTracePath, O_WRONLY|O_APPEND|O_CREAT, DEFFILEMODE);
if (fd < 0) {
return;
}
snprintf(tmpBuffer, sizeof(tmpBuffer), "%s\n", toolArgs->prelinkedKernelPath);
tmpBufLen = strlen(tmpBuffer);
writeSize = write(fd, tmpBuffer, tmpBufLen);
if (writeSize != (ssize_t)tmpBufLen) {
close(fd);
return;
}
count = CFArrayGetCount(prelinkKexts);
for (i = 0; i < count; i++) {
CFStringRef kextID;
OSKextRef theKext = (OSKextRef)CFArrayGetValueAtIndex(prelinkKexts, i);
kextID = OSKextGetIdentifier(theKext);
if (kextID) {
char kextIDCString[KMOD_MAX_NAME];
CFStringGetCString(kextID, kextIDCString, sizeof(kextIDCString),
kCFStringEncodingUTF8);
snprintf(tmpBuffer, sizeof(tmpBuffer), "[Logging for XBS] Used kext bundle identifier: %s\n", kextIDCString);
tmpBufLen = strlen(tmpBuffer);
writeSize = write(fd, tmpBuffer, tmpBufLen);
if (writeSize != (ssize_t)tmpBufLen) {
close(fd);
return;
}
}
}
close(fd);
}
ExitStatus readPrelinkedKernelArgs(
KcgenArgs * toolArgs,
int argc,
char * const * argv,
Boolean isLongopt)
{
char * filename = NULL;
if (optarg) {
filename = optarg;
} else if (isLongopt && optind < argc) {
filename = argv[optind];
optind++;
}
if (filename && !filename[0]) {
filename = NULL;
}
return setPrelinkedKernelArgs(toolArgs, filename);
}
ExitStatus setPrelinkedKernelArgs(
KcgenArgs * toolArgs,
char * filename)
{
ExitStatus result = EX_USAGE;
if (toolArgs->prelinkedKernelPath) {
OSKextLog( 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 (!filename) {
OSKextLog( 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( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Error - prelinked kernel filename length exceeds PATH_MAX");
goto finish;
}
}
result = EX_OK;
finish:
return result;
}
void addArch(
KcgenArgs * toolArgs,
const NXArchInfo * arch)
{
if (CFArrayContainsValue(toolArgs->targetArchs,
RANGE_ALL(toolArgs->targetArchs), arch))
{
return;
}
CFArrayAppendValue(toolArgs->targetArchs, arch);
}
const NXArchInfo * addArchForName(
KcgenArgs * toolArgs,
const char * archname)
{
const NXArchInfo * result = NULL;
result = NXGetArchInfoFromName(archname);
if (!result) {
goto finish;
}
addArch(toolArgs, result);
finish:
return result;
}
ExitStatus checkArgs(KcgenArgs * toolArgs)
{
ExitStatus result = EX_USAGE;
if (!toolArgs->prelinkedKernelPath)
{
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Error - no work to do; check options and try again.");
goto finish;
}
if (!CFArrayGetCount(toolArgs->argURLs) &&
!toolArgs->compress && !toolArgs->uncompress)
{
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Error - no kexts or directories specified.");
goto finish;
}
if (!toolArgs->compress && !toolArgs->uncompress) {
toolArgs->compress = true;
toolArgs->compressionType = COMP_TYPE_LZSS;
} else if (toolArgs->compress && toolArgs->uncompress) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Both -%s and -%s specified; using -%s.",
kOptNameCompressed, kOptNameUncompressed, kOptNameCompressed);
toolArgs->compress = true;
toolArgs->uncompress = false;
}
result = EX_OK;
finish:
if (result == EX_USAGE) {
usage(kUsageLevelBrief);
}
return result;
}
typedef struct {
KcgenArgs * toolArgs;
CFMutableArrayRef kextArray;
const NXArchInfo * arch;
Boolean optional;
Boolean error;
} FilterIDContext;
void filterKextID(const void * vValue, void * vContext)
{
CFStringRef kextID = (CFStringRef)vValue;
FilterIDContext * context = (FilterIDContext *)vContext;
OSKextRef theKext = OSKextGetKextWithIdentifier(kextID);
if (!theKext) {
char kextIDCString[KMOD_MAX_NAME];
CFStringGetCString(kextID, kextIDCString, sizeof(kextIDCString),
kCFStringEncodingUTF8);
if (context->optional) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Can't find kext with optional identifier %s; skipping.", kextIDCString);
} else {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Error - can't find kext with identifier %s.", kextIDCString);
context->error = TRUE;
}
goto finish;
}
if (checkKextForProblems(context->toolArgs, theKext, context->arch)) {
if (!context->optional) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Error - a required kext was omitted");
context->error = true;
}
goto finish;
}
if (!CFArrayContainsValue(context->kextArray,
RANGE_ALL(context->kextArray), theKext))
{
CFArrayAppendValue(context->kextArray, theKext);
}
finish:
return;
}
ExitStatus checkKextForProblems(
KcgenArgs * toolArgs,
OSKextRef theKext,
const NXArchInfo * arch)
{
ExitStatus result = EX_SOFTWARE;
char kextPath[PATH_MAX];
if (!CFURLGetFileSystemRepresentation(OSKextGetURL(theKext),
false, (UInt8 *)kextPath, sizeof(kextPath)))
{
strlcpy(kextPath, "(unknown)", sizeof(kextPath));
}
if (!OSKextSupportsArchitecture(theKext, arch)) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
"%s doesn't support architecture '%s'; ommiting.", kextPath,
arch->name);
goto finish;
}
if (!OSKextIsValid(theKext)) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogArchiveFlag |
kOSKextLogValidationFlag | kOSKextLogGeneralFlag,
"%s is not valid; omitting.", kextPath);
if (toolArgs->printTestResults) {
OSKextLogDiagnostics(theKext, kOSKextDiagnosticsFlagAll);
}
goto finish;
}
if (!OSKextResolveDependencies(theKext)) {
OSKextLog( NULL,
kOSKextLogWarningLevel | kOSKextLogArchiveFlag |
kOSKextLogDependenciesFlag | kOSKextLogGeneralFlag,
"%s is missing dependencies (including anyway; "
"dependencies may be available from elsewhere)", kextPath);
if (toolArgs->printTestResults) {
OSKextLogDiagnostics(theKext, kOSKextDiagnosticsFlagAll);
}
}
result = EX_OK;
finish:
return result;
}
ExitStatus filterKextsForCache(
KcgenArgs * toolArgs,
CFMutableArrayRef kextArray,
const NXArchInfo * arch,
Boolean * fatalOut __unused)
{
ExitStatus result = EX_SOFTWARE;
CFIndex count, i;
CFArrayRemoveAllValues(kextArray);
if (CFSetGetCount(toolArgs->kextIDs) || CFSetGetCount(toolArgs->optionalKextIDs)) {
FilterIDContext context;
context.toolArgs = toolArgs;
context.kextArray = kextArray;
context.arch = arch;
context.optional = FALSE;
context.error = FALSE;
CFSetApplyFunction(toolArgs->kextIDs, filterKextID, &context);
context.optional = TRUE;
CFSetApplyFunction(toolArgs->optionalKextIDs, filterKextID, &context);
if (context.error) {
goto finish;
}
} else {
count = CFArrayGetCount(toolArgs->repositoryKexts);
for (i = 0; i < count; i++) {
char kextPath[PATH_MAX];
OSKextRef theKext = (OSKextRef)CFArrayGetValueAtIndex(
toolArgs->repositoryKexts, i);
if (!CFArrayContainsValue(kextArray, RANGE_ALL(kextArray), theKext) &&
!checkKextForProblems(toolArgs, theKext, arch)) {
CFArrayAppendValue(kextArray, theKext);
}
}
count = CFArrayGetCount(toolArgs->namedKexts);
for (i = 0; i < count; i++) {
OSKextRef theKext = (OSKextRef)CFArrayGetValueAtIndex(
toolArgs->namedKexts, i);
if (!CFArrayContainsValue(kextArray, RANGE_ALL(kextArray), theKext) &&
!checkKextForProblems(toolArgs, theKext, arch)) {
CFArrayAppendValue(kextArray, theKext);
}
}
}
result = EX_OK;
finish:
return result;
}
ExitStatus
createPrelinkedKernelArchs(
KcgenArgs * toolArgs,
CFMutableArrayRef * prelinkArchsOut)
{
ExitStatus result = EX_OSERR;
CFMutableArrayRef kernelArchs = NULL; CFMutableArrayRef prelinkArchs = NULL; const NXArchInfo * targetArch = NULL; int i = 0;
result = readFatFileArchsWithPath(toolArgs->kernelPath, &kernelArchs);
if (result != EX_OK) {
goto finish;
}
prelinkArchs = CFArrayCreateMutableCopy(kCFAllocatorDefault,
0, toolArgs->targetArchs);
if (!prelinkArchs) {
OSKextLogMemError();
result = EX_OSERR;
goto finish;
}
for (i = 0; i < CFArrayGetCount(prelinkArchs); ++i) {
targetArch = CFArrayGetValueAtIndex(prelinkArchs, i);
if (!CFArrayContainsValue(kernelArchs,
RANGE_ALL(kernelArchs), targetArch))
{
OSKextLog( NULL,
kOSKextLogWarningLevel | kOSKextLogArchiveFlag,
"Warning - kernel file %s does not contain requested arch: %s",
toolArgs->kernelPath, targetArch->name);
CFArrayRemoveValueAtIndex(prelinkArchs, i);
i--;
continue;
}
}
*prelinkArchsOut = (CFMutableArrayRef) CFRetain(prelinkArchs);
result = EX_OK;
finish:
SAFE_RELEASE(kernelArchs);
SAFE_RELEASE(prelinkArchs);
return result;
}
ExitStatus
createPrelinkedKernel(
KcgenArgs * toolArgs)
{
ExitStatus result = EX_OSERR;
struct timeval prelinkFileTimes[2];
CFMutableArrayRef generatedArchs = NULL; CFMutableArrayRef generatedSymbols = NULL; CFMutableArrayRef existingArchs = NULL; CFMutableArrayRef existingSlices = NULL; CFMutableArrayRef prelinkArchs = NULL; CFMutableArrayRef prelinkSlices = NULL; CFDataRef prelinkSlice = NULL; CFDictionaryRef sliceSymbols = NULL; const NXArchInfo * targetArch = NULL; Boolean updateModTime = false;
u_int numArchs = 0;
u_int i = 0;
int j = 0;
bzero(prelinkFileTimes, sizeof(prelinkFileTimes));
result = createPrelinkedKernelArchs(toolArgs, &prelinkArchs);
if (result != EX_OK) {
goto finish;
}
numArchs = (u_int)CFArrayGetCount(prelinkArchs);
prelinkSlices = CFArrayCreateMutable(kCFAllocatorDefault,
numArchs, &kCFTypeArrayCallBacks);
generatedSymbols = CFArrayCreateMutable(kCFAllocatorDefault,
numArchs, &kCFTypeArrayCallBacks);
generatedArchs = CFArrayCreateMutable(kCFAllocatorDefault,
numArchs, NULL);
if (!prelinkSlices || !generatedSymbols || !generatedArchs) {
OSKextLogMemError();
result = EX_OSERR;
goto finish;
}
for (i = 0; i < numArchs; i++) {
targetArch = CFArrayGetValueAtIndex(prelinkArchs, i);
SAFE_RELEASE_NULL(prelinkSlice);
SAFE_RELEASE_NULL(sliceSymbols);
if (existingArchs &&
targetArch != OSKextGetRunningKernelArchitecture())
{
j = (int)CFArrayGetFirstIndexOfValue(existingArchs,
RANGE_ALL(existingArchs), targetArch);
if (j != -1) {
prelinkSlice = CFArrayGetValueAtIndex(existingSlices, j);
CFArrayAppendValue(prelinkSlices, prelinkSlice);
prelinkSlice = NULL;
OSKextLog( NULL,
kOSKextLogDebugLevel | kOSKextLogArchiveFlag,
"Using existing prelinked slice for arch %s",
targetArch->name);
continue;
}
}
OSKextLog( NULL,
kOSKextLogDebugLevel | kOSKextLogArchiveFlag,
"Generating a new prelinked slice for arch %s",
targetArch->name);
result = createPrelinkedKernelForArch(toolArgs, &prelinkSlice,
&sliceSymbols, targetArch);
if (result != EX_OK) {
goto finish;
}
if (toolArgs->maxSliceSize &&
(CFDataGetLength(prelinkSlice) > toolArgs->maxSliceSize)) {
result = EX_SOFTWARE;
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Error - prelink slice is larger (%ld) than requested maximum %ld.",
(long int)CFDataGetLength(prelinkSlice),
(long int)toolArgs->maxSliceSize);
goto finish;
}
CFArrayAppendValue(prelinkSlices, prelinkSlice);
CFArrayAppendValue(generatedSymbols, sliceSymbols);
CFArrayAppendValue(generatedArchs, targetArch);
}
result = writeFatFile(toolArgs->prelinkedKernelPath, prelinkSlices,
prelinkArchs, (0644), (updateModTime) ? prelinkFileTimes : NULL);
if (result != EX_OK) {
goto finish;
}
if (toolArgs->symbolDirURL) {
result = writePrelinkedSymbols(toolArgs->symbolDirURL,
generatedSymbols, generatedArchs);
if (result != EX_OK) {
goto finish;
}
}
OSKextLog( NULL,
kOSKextLogBasicLevel | kOSKextLogGeneralFlag | kOSKextLogArchiveFlag,
"Created prelinked kernel %s.",
toolArgs->prelinkedKernelPath);
result = EX_OK;
finish:
SAFE_RELEASE(generatedArchs);
SAFE_RELEASE(generatedSymbols);
SAFE_RELEASE(existingArchs);
SAFE_RELEASE(existingSlices);
SAFE_RELEASE(prelinkArchs);
SAFE_RELEASE(prelinkSlices);
SAFE_RELEASE(prelinkSlice);
SAFE_RELEASE(sliceSymbols);
return result;
}
ExitStatus createPrelinkedKernelForArch(
KcgenArgs * toolArgs,
CFDataRef * prelinkedKernelOut,
CFDictionaryRef * prelinkedSymbolsOut,
const NXArchInfo * archInfo)
{
ExitStatus result = EX_OSERR;
CFMutableArrayRef prelinkKexts = NULL;
CFDataRef kernelImage = NULL;
CFDataRef prelinkedKernel = NULL;
uint32_t flags = 0;
Boolean fatalOut = false;
kernelImage = readMachOSliceForArch(toolArgs->kernelPath, archInfo, TRUE);
if (!kernelImage) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogArchiveFlag | kOSKextLogFileAccessFlag,
"Error - failed to read kernel file.");
goto finish;
}
if (!OSKextSetArchitecture(archInfo)) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Error - can't set architecture %s to create prelinked kernel.",
archInfo->name);
result = EX_OSERR;
goto finish;
}
prelinkKexts = CFArrayCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeArrayCallBacks);
if (!prelinkKexts) {
OSKextLogMemError();
result = EX_OSERR;
goto finish;
}
result = filterKextsForCache(toolArgs, prelinkKexts,
archInfo, &fatalOut);
if (result != EX_OK || fatalOut) {
goto finish;
}
result = EX_OSERR;
if (!CFArrayGetCount(prelinkKexts)) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
"Error - no kexts found for architecture %s.",
archInfo->name);
goto finish;
}
flags |= kOSKextKernelcacheKASLRFlag;
flags |= kOSKextKernelcacheNeedAllFlag;
flags |= kOSKextKernelcacheSkipAuthenticationFlag;
flags |= kOSKextKernelcacheIncludeAllPersonalitiesFlag;
flags |= (toolArgs->stripSymbols) ? kOSKextKernelcacheStripSymbolsFlag : 0;
flags |= (toolArgs->printTestResults) ? kOSKextKernelcachePrintDiagnosticsFlag : 0;
prelinkedKernel = OSKextCreatePrelinkedKernel(kernelImage, prelinkKexts,
toolArgs->volumeRootURL, flags, prelinkedSymbolsOut);
if (!prelinkedKernel) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
"Error - failed to generate prelinked kernel.");
result = EX_OSERR;
goto finish;
}
logUsedKexts(toolArgs, prelinkKexts);
if (toolArgs->compress) {
*prelinkedKernelOut = compressPrelinkedSlice(toolArgs->compressionType,
prelinkedKernel,
true);
} else {
*prelinkedKernelOut = CFRetain(prelinkedKernel);
}
if (!*prelinkedKernelOut) {
goto finish;
}
result = EX_OK;
finish:
SAFE_RELEASE(kernelImage);
SAFE_RELEASE(prelinkKexts);
SAFE_RELEASE(prelinkedKernel);
return result;
}
ExitStatus compressPrelinkedKernel(
const char * prelinkPath,
Boolean compress,
uint32_t compressionType)
{
ExitStatus result = EX_SOFTWARE;
struct timeval prelinkedKernelTimes[2];
CFMutableArrayRef prelinkedSlices = NULL; CFMutableArrayRef prelinkedArchs = NULL; CFDataRef prelinkedSlice = NULL; const NXArchInfo * archInfo = NULL; const u_char * sliceBytes = NULL; mode_t fileMode = 0;
int i = 0;
result = readMachOSlices(prelinkPath, &prelinkedSlices,
&prelinkedArchs, &fileMode, prelinkedKernelTimes);
if (result != EX_OK) {
goto finish;
}
for (i = 0; i < CFArrayGetCount(prelinkedSlices); ++i) {
SAFE_RELEASE_NULL(prelinkedSlice);
prelinkedSlice = CFArrayGetValueAtIndex(prelinkedSlices, i);
if (compress) {
prelinkedSlice = compressPrelinkedSlice(compressionType,
prelinkedSlice,
true);
if (!prelinkedSlice) {
result = EX_DATAERR;
goto finish;
}
} else {
prelinkedSlice = uncompressPrelinkedSlice(prelinkedSlice);
if (!prelinkedSlice) {
result = EX_DATAERR;
goto finish;
}
}
CFArraySetValueAtIndex(prelinkedSlices, i, prelinkedSlice);
}
SAFE_RELEASE_NULL(prelinkedSlice);
if (!prelinkedArchs && CFArrayGetCount(prelinkedSlices) == 1) {
if (!createCFMutableArray(&prelinkedArchs, NULL)) {
OSKextLogMemError();
result = EX_OSERR;
goto finish;
}
sliceBytes = CFDataGetBytePtr(CFArrayGetValueAtIndex(prelinkedSlices, 0));
archInfo = getThinHeaderPageArch(sliceBytes);
if (archInfo) {
CFArrayAppendValue(prelinkedArchs, archInfo);
} else {
SAFE_RELEASE_NULL(prelinkedArchs);
}
}
if (!prelinkedArchs) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
"Couldn't determine prelinked kernel's architecture");
result = EX_SOFTWARE;
goto finish;
}
result = writeFatFile(prelinkPath, prelinkedSlices,
prelinkedArchs, fileMode, prelinkedKernelTimes);
if (result != EX_OK) {
goto finish;
}
result = EX_OK;
finish:
SAFE_RELEASE(prelinkedSlices);
SAFE_RELEASE(prelinkedArchs);
SAFE_RELEASE(prelinkedSlice);
return result;
}
void usage(UsageLevel usageLevel)
{
fprintf(stderr,
"usage: %1$s -prelinked-kernel <filename> [options] [--] [kext or directory]\n"
"\n",
progname);
if (usageLevel == kUsageLevelBrief) {
fprintf(stderr, "use %s -%s for an explanation of each option\n",
progname, kOptNameHelp);
}
if (usageLevel == kUsageLevelBrief) {
return;
}
fprintf(stderr, "-%s <filename> (-%c):\n"
" create/update prelinked kernel\n",
kOptNamePrelinkedKernel, kOptPrelinkedKernel);
fprintf(stderr, "\n");
fprintf(stderr,
"kext or directory: Consider kext or all kexts in directory for inclusion\n");
fprintf(stderr, "-%s <bundle_id> (-%c):\n"
" include the kext whose CFBundleIdentifier is <bundle_id>\n",
kOptNameBundleIdentifier, kOptBundleIdentifier);
fprintf(stderr, "-%s <bundle_id>:\n"
" include the kext whose CFBundleIdentifier is <bundle_id> if it exists\n",
kOptNameOptionalBundleIdentifier);
fprintf(stderr, "-%s <kernel_filename> (-%c): Use kernel_filename for a prelinked kernel\n",
kOptNameKernel, kOptKernel);
fprintf(stderr, "-%s <archname>:\n"
" include architecture <archname> in created cache(s)\n",
kOptNameArch);
fprintf(stderr, "-%s <volume>:\n"
" Save kext paths in the prelinked kernel relative to <volume>\n",
kOptNameVolumeRoot);
fprintf(stderr, "\n");
fprintf(stderr, "-%s (-%c): quiet mode: print no informational or error messages\n",
kOptNameQuiet, kOptQuiet);
fprintf(stderr, "-%s [ 0-6 | 0x<flags> ] (-%c):\n"
" verbose mode; print info about analysis & loading\n",
kOptNameVerbose, kOptVerbose);
fprintf(stderr, "\n");
fprintf(stderr, "-%s (-%c):\n"
" print diagnostics for kexts with problems\n",
kOptNameTests, kOptTests);
fprintf(stderr, "-%s (-%c): print this message and exit\n",
kOptNameHelp, kOptHelp);
return;
}