Loading...
--- dyld/dyld-1340/kernel-collection-builder/dyld_app_cache_util.cpp
+++ /dev/null
@@ -1,1873 +0,0 @@
-/*
- * Copyright (c) 2017 Apple 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 <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <list>
-#include <map>
-#include <vector>
-
-#include <variant>
-
-#include <CoreFoundation/CFBundle.h>
-#include <CoreFoundation/CFNumber.h>
-#include <CoreFoundation/CFPropertyList.h>
-#include <CoreFoundation/CFStream.h>
-
-#include "kernel_collection_builder.h"
-#include "ClosureFileSystemPhysical.h"
-#include "FileUtils.h"
-#include "Header.h"
-#include "GradedArchitectures.h"
-#include "JSONWriter.h"
-#include "MachOAppCache.h"
-
-using namespace json;
-
-using dyld3::closure::FileSystemPhysical;
-using dyld3::closure::LoadedFileInfo;
-using dyld3::MachOAnalyzer;
-using dyld3::MachOAppCache;
-using json::Node;
-
-using mach_o::GradedArchitectures;
-using mach_o::Header;
-using mach_o::Platform;
-
-__attribute__((__noreturn__))
-static void exit_usage(const char* missingOption = nullptr) {
- if ( missingOption != nullptr ) {
- fprintf(stderr, "Missing option '%s'\n", missingOption);
- fprintf(stderr, "\n");
- }
-
- fprintf(stderr, "Usage: dyld_app_cache_util [-layout] [-entrypoint] [-fixups] [-symbols] [-kmod] [-uuid] [-fips] -arch arch -platform platform -app-cache app-cache-path\n");
- fprintf(stderr, " -layout print the layout of an existing app cache\n");
- fprintf(stderr, " -entrypoint print the entrypoint of an existing app cache\n");
- fprintf(stderr, " -fixups print the fixups of an existing app cache\n");
- fprintf(stderr, " -symbols print the symbols of an existing app cache\n");
- fprintf(stderr, " -kmod print the kmod_info of an existing app cache\n");
- fprintf(stderr, " -uuid print the UUID of an existing app cache\n");
- fprintf(stderr, " -fips print the FIPS section of an existing app cache\n");
- fprintf(stderr, " -function-starts print the function starts of an app cache\n");
- fprintf(stderr, "\n");
-
- fprintf(stderr, "Usage: dyld_app_cache_util -validate file-path -arch arch -platform platform\n");
- fprintf(stderr, " -validate the path to check is valid for inserting in to an app cache\n");
- fprintf(stderr, "\n");
-
- fprintf(stderr, "Usage: dyld_app_cache_util -list-bundles directory-path\n");
- fprintf(stderr, " -list-bundles the directory to index for bundles\n");
- fprintf(stderr, "\n");
-
- fprintf(stderr, "Usage: dyld_app_cache_util -create-kernel-collection kernel-collection -kernel kernel-path [-extensions path-to-extensions] [-bundle-id bundle-id]*\n");
- fprintf(stderr, " -create-kernel-collection create a kernel collection and write to the given path\n");
- fprintf(stderr, " -kernel path to the kernel static executable\n");
- fprintf(stderr, " -extensions path to the kernel extensions directory\n");
- fprintf(stderr, " -bundle-id zero or more bundle-ids to link in to the kernel collection\n");
- fprintf(stderr, " -sectcreate segment name, section name, and payload file path for more data to embed in the kernel\n");
- fprintf(stderr, "\n");
-
- fprintf(stderr, "Usage: dyld_app_cache_util -create-pageable-kernel-collection aux-kernel-collection -kernel-collection kernel-collection-path [-extensions path-to-extensions] [-bundle-id bundle-id]*\n");
- fprintf(stderr, " -create-pageable-kernel-collection create a pageable kernel collection and write to the given path\n");
- fprintf(stderr, " -kernel-collection path to the kernel collection collection\n");
- fprintf(stderr, " -extensions path to the kernel extensions directory\n");
- fprintf(stderr, " -bundle-id zero or more bundle-ids to link in to the kernel collection\n");
- fprintf(stderr, "\n");
-
- fprintf(stderr, "Usage: dyld_app_cache_util -create-aux-kernel-collection aux-kernel-collection -kernel-collection kernel-collection-path [-extensions path-to-extensions] [-bundle-id bundle-id]*\n");
- fprintf(stderr, " -create-aux-kernel-collection create an aux kernel collection and write to the given path\n");
- fprintf(stderr, " -kernel-collection path to the kernel collection\n");
- fprintf(stderr, " -pageable-collection path to the pageable collection\n");
- fprintf(stderr, " -extensions path to the kernel extensions directory\n");
- fprintf(stderr, " -bundle-id zero or more bundle-ids to link in to the kernel collection\n");
- fprintf(stderr, "\n");
-
- fprintf(stderr, "Common options:\n");
- fprintf(stderr, " -arch xxx the arch to use to create the app cache\n");
- fprintf(stderr, " -platform xxx the platform to use to create the app cache\n");
-
- exit(1);
-}
-
-struct DumpOptions {
- bool printLayout = false;
- bool printEntryPoint = false;
- bool printFixups = false;
- bool printSymbols = false;
- bool printUUID = false;
- bool printPrelinkInfo = false;
- bool printKModInfo = false;
- bool printFIPS = false;
- bool printFunctionStarts = false;
-};
-
-struct ValidateOptions {
- const char* filePath = nullptr;
-};
-
-struct ListBundlesOptions {
- const char* directoryPath = nullptr;
-};
-
-// The payload of -sectcreate
-struct SectionData {
- const char* segmentName = nullptr;
- const char* sectionName = nullptr;
- const char* payloadFilePath = nullptr;
-};
-
-struct CreateKernelCollectionOptions {
- const char* outputCachePath = nullptr;
- const char* kernelPath = nullptr;
- const char* kernelCollectionPath = nullptr;
- const char* pageableCollectionPath = nullptr;
- const char* extensionsPath = nullptr;
- const char* volumeRoot = "";
- std::vector<const char*> bundleIDs;
- bool verbose = false;
- bool printJSONErrors = false;
- CollectionKind collectionKind = unknownKC;
- StripMode stripMode = unknownStripMode;
- std::vector<SectionData> sections;
- const char* prelinkInfoExtraData = nullptr;
-};
-
-typedef std::variant<std::monostate, DumpOptions, ValidateOptions, ListBundlesOptions, CreateKernelCollectionOptions> OptionsVariants;
-
-struct CommonOptions {
- const char* appCachePath = nullptr;
- std::vector<const char*> archs;
- const char* platform = nullptr;
-};
-
-CommonOptions gOpts;
-
-template<typename T>
-static T& exitOrGetState(OptionsVariants& options, const char* argv) {
- if (std::holds_alternative<std::monostate>(options)) {
- return options.emplace<T>();
- }
- if (std::holds_alternative<T>(options))
- return std::get<T>(options);
- exit_usage();
-}
-
-static bool parseArgs(int argc, const char* argv[], OptionsVariants& options) {
- for (int i = 1; i < argc; ++i) {
- const char* arg = argv[i];
- if (arg[0] != '-') {
- fprintf(stderr, "unknown option: %s\n", arg);
- exit_usage();
- }
-
- // Common options
- if (strcmp(arg, "-app-cache") == 0) {
- if (gOpts.appCachePath != nullptr)
- exit_usage();
- gOpts.appCachePath = argv[++i];
- continue;
- }
- if (strcmp(arg, "-arch") == 0) {
- gOpts.archs.push_back(argv[++i]);
- continue;
- }
- if (strcmp(arg, "-platform") == 0) {
- if (gOpts.platform != nullptr)
- exit_usage();
- gOpts.platform = argv[++i];
- continue;
- }
-
- // DumpOptions
- if (strcmp(arg, "-layout") == 0) {
- exitOrGetState<DumpOptions>(options, arg).printLayout = true;
- continue;
- }
- if (strcmp(arg, "-entrypoint") == 0) {
- exitOrGetState<DumpOptions>(options, arg).printEntryPoint = true;
- continue;
- }
- if (strcmp(arg, "-fixups") == 0) {
- exitOrGetState<DumpOptions>(options, arg).printFixups = true;
- continue;
- }
- if (strcmp(arg, "-symbols") == 0) {
- exitOrGetState<DumpOptions>(options, arg).printSymbols = true;
- continue;
- }
- if (strcmp(arg, "-uuid") == 0) {
- exitOrGetState<DumpOptions>(options, arg).printUUID = true;
- continue;
- }
- if (strcmp(arg, "-prelink-info") == 0) {
- exitOrGetState<DumpOptions>(options, arg).printPrelinkInfo = true;
- continue;
- }
- if (strcmp(arg, "-kmod") == 0) {
- exitOrGetState<DumpOptions>(options, arg).printKModInfo = true;
- continue;
- }
- if (strcmp(arg, "-fips") == 0) {
- exitOrGetState<DumpOptions>(options, arg).printFIPS = true;
- continue;
- }
- if (strcmp(arg, "-function-starts") == 0) {
- exitOrGetState<DumpOptions>(options, arg).printFunctionStarts = true;
- continue;
- }
-
- // ValidateOptions
- if (strcmp(arg, "-validate") == 0) {
- exitOrGetState<ValidateOptions>(options, arg).filePath = argv[++i];
- continue;
- }
-
- // ListBundlesOptions
- if (strcmp(arg, "-list-bundles") == 0) {
- exitOrGetState<ListBundlesOptions>(options, arg).directoryPath = argv[++i];
- continue;
- }
-
- // CreateKernelCollectionOptions
- if (strcmp(arg, "-create-kernel-collection") == 0) {
- exitOrGetState<CreateKernelCollectionOptions>(options, arg).outputCachePath = argv[++i];
- exitOrGetState<CreateKernelCollectionOptions>(options, arg).collectionKind = baseKC;
- continue;
- }
- if (strcmp(arg, "-kernel") == 0) {
- exitOrGetState<CreateKernelCollectionOptions>(options, arg).kernelPath = argv[++i];
- continue;
- }
- if (strcmp(arg, "-create-pageable-kernel-collection") == 0) {
- exitOrGetState<CreateKernelCollectionOptions>(options, arg).outputCachePath = argv[++i];
- exitOrGetState<CreateKernelCollectionOptions>(options, arg).collectionKind = pageableKC;
- continue;
- }
- if (strcmp(arg, "-create-aux-kernel-collection") == 0) {
- exitOrGetState<CreateKernelCollectionOptions>(options, arg).outputCachePath = argv[++i];
- exitOrGetState<CreateKernelCollectionOptions>(options, arg).collectionKind = auxKC;
- continue;
- }
- if (strcmp(arg, "-kernel-collection") == 0) {
- exitOrGetState<CreateKernelCollectionOptions>(options, arg).kernelCollectionPath = argv[++i];
- continue;
- }
- if (strcmp(arg, "-pageable-collection") == 0) {
- exitOrGetState<CreateKernelCollectionOptions>(options, arg).pageableCollectionPath = argv[++i];
- continue;
- }
- if (strcmp(arg, "-extensions") == 0) {
- exitOrGetState<CreateKernelCollectionOptions>(options, arg).extensionsPath = argv[++i];
- continue;
- }
- if (strcmp(arg, "-volume-root") == 0) {
- exitOrGetState<CreateKernelCollectionOptions>(options, arg).volumeRoot = argv[++i];
- continue;
- }
- if ( (strcmp(arg, "-bundle-id") == 0) || (strcmp(arg, "-b") == 0) ) {
- exitOrGetState<CreateKernelCollectionOptions>(options, arg).bundleIDs.push_back(argv[++i]);
- continue;
- }
- if (strcmp(arg, "-verbose") == 0) {
- exitOrGetState<CreateKernelCollectionOptions>(options, arg).verbose = true;
- continue;
- }
- if (strcmp(arg, "-json-errors") == 0) {
- exitOrGetState<CreateKernelCollectionOptions>(options, arg).printJSONErrors = true;
- continue;
- }
- if (strcmp(arg, "-strip-all") == 0) {
- exitOrGetState<CreateKernelCollectionOptions>(options, arg).stripMode = stripAll;
- continue;
- }
- if (strcmp(arg, "-strip-all-kexts") == 0) {
- exitOrGetState<CreateKernelCollectionOptions>(options, arg).stripMode = stripAllKexts;
- continue;
- }
- if (strcmp(arg, "-sectcreate") == 0) {
- const char* segmentName = argv[++i];
- const char* sectionName = argv[++i];
- const char* payloadFilePath = argv[++i];
- SectionData sectData = { segmentName, sectionName, payloadFilePath };
- exitOrGetState<CreateKernelCollectionOptions>(options, arg).sections.push_back(sectData);
- continue;
- }
- if (strcmp(arg, "-prelink-info-extra") == 0) {
- const char* payloadFilePath = argv[++i];
- exitOrGetState<CreateKernelCollectionOptions>(options, arg).prelinkInfoExtraData = payloadFilePath;
- continue;
- }
-
-
- fprintf(stderr, "unknown option: %s\n", arg);
- exit_usage();
- }
-
- return true;
-}
-
-static int dumpAppCache(const DumpOptions& options) {
- // Verify any required options
- if (gOpts.archs.size() != 1)
- exit_usage("-arch");
- if (gOpts.platform == nullptr)
- exit_usage("-platform");
-
- if (gOpts.appCachePath == nullptr)
- exit_usage();
-
- FileSystemPhysical fileSystem;
- if (!fileSystem.fileExists(gOpts.appCachePath)) {
- fprintf(stderr, "App-cache path does not exist: %s\n", gOpts.appCachePath);
- return 1;
- }
-
- const GradedArchitectures& archs = GradedArchitectures::forName(gOpts.archs[0], /*keysOff=*/false, /*isKernel=*/true);
- Platform platform = Platform();
-
- // HACK: Pass a real option for building a kernel app cache
- if (strcmp(gOpts.platform, "kernel") != 0) {
- platform = Platform::byName(gOpts.platform);
- if ( platform.empty() ) {
- fprintf(stderr, "Could not create app cache because: unknown platform '%s'\n", gOpts.platform);
- return 1;
- }
- }
-
- __block Diagnostics diag;
- char appCacheRealPath[MAXPATHLEN];
- LoadedFileInfo loadedFileInfo = MachOAnalyzer::load(diag, fileSystem, gOpts.appCachePath, archs, platform, appCacheRealPath);
- if (diag.hasError()) {
- fprintf(stderr, "Could not load app cache because: %s\n", diag.errorMessage().c_str());
- return 1;
- }
-
- MachOAppCache* appCacheMA = (MachOAppCache*)loadedFileInfo.fileContent;
- if (appCacheMA == nullptr) {
- fprintf(stderr, "Could not load app cache: %s\n", gOpts.appCachePath);
- return 1;
- }
-
- if (options.printLayout) {
- __block Node topNode;
-
- // Add the segments for the app cache
- __block Node appCacheSegmentsNode;
- __block bool hasError = false;
- ((const Header*)appCacheMA)->forEachSegment(^(const Header::SegmentInfo &info, bool &stopSegment) {
- Node segmentNode;
- segmentNode.map["name"] = makeNode(info.segmentName.data());
- segmentNode.map["vmAddr"] = makeNode(hex(info.vmaddr));
- segmentNode.map["vmSize"] = makeNode(hex(info.vmsize));
- segmentNode.map["vmEnd"] = makeNode(hex(info.vmaddr + info.vmsize));
- switch (info.initProt) {
- case VM_PROT_READ:
- segmentNode.map["permissions"] = makeNode("r--");
- break;
- case VM_PROT_WRITE:
- segmentNode.map["permissions"] = makeNode("-w-");
- break;
- case VM_PROT_EXECUTE:
- segmentNode.map["permissions"] = makeNode("--x");
- break;
- case VM_PROT_READ | VM_PROT_WRITE:
- segmentNode.map["permissions"] = makeNode("rw-");
- break;
- case VM_PROT_READ | VM_PROT_EXECUTE:
- segmentNode.map["permissions"] = makeNode("r-x");
- break;
- case VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE:
- segmentNode.map["permissions"] = makeNode("rwx");
- break;
- default:
- fprintf(stderr, "Unknown permissions on segment '%.*s'\n", (int)info.segmentName.size(), info.segmentName.data());
- hasError = true;
- stopSegment = true;
- }
-
- __block Node sectionsNode;
- ((const Header*)appCacheMA)->forEachSection(^(const Header::SectionInfo §Info, bool &stopSection) {
- if ( sectInfo.segmentName != info.segmentName )
- return;
-
- Node sectionNode;
- sectionNode.map["name"] = makeNode(std::string(sectInfo.sectionName));
- sectionNode.map["vmAddr"] = makeNode(hex(sectInfo.address));
- sectionNode.map["vmSize"] = makeNode(hex(sectInfo.size));
- sectionNode.map["vmEnd"] = makeNode(hex(sectInfo.address + sectInfo.size));
-
- sectionsNode.array.push_back(sectionNode);
- });
-
- if ( !sectionsNode.array.empty() ) {
- segmentNode.map["sections"] = sectionsNode;
- }
-
- appCacheSegmentsNode.array.push_back(segmentNode);
- });
-
- if (hasError)
- return 1;
-
- topNode.map["cache-segments"] = appCacheSegmentsNode;
-
- // Map from name to relative path
- __block std::unordered_map<std::string, std::string> relativePaths;
- appCacheMA->forEachPrelinkInfoLibrary(diag, ^(const char *bundleName, const char* relativePath,
- const std::vector<const char *> &deps) {
- if ( relativePath != nullptr )
- relativePaths[bundleName] = relativePath;
- });
-
- __block Node dylibsNode;
- appCacheMA->forEachDylib(diag, ^(const MachOAnalyzer *ma, const char *name, bool &stop) {
- __block Node segmentsNode;
- ((const Header*)ma)->forEachSegment(^(const Header::SegmentInfo &info, bool &stopSegment) {
- Node segmentNode;
- segmentNode.map["name"] = makeNode(info.segmentName.data());
- segmentNode.map["vmAddr"] = makeNode(hex(info.vmaddr));
- segmentNode.map["vmSize"] = makeNode(hex(info.vmsize));
- segmentNode.map["vmEnd"] = makeNode(hex(info.vmaddr + info.vmsize));
-
- switch (info.initProt) {
- case VM_PROT_READ:
- segmentNode.map["permissions"] = makeNode("r--");
- break;
- case VM_PROT_WRITE:
- segmentNode.map["permissions"] = makeNode("-w-");
- break;
- case VM_PROT_EXECUTE:
- segmentNode.map["permissions"] = makeNode("--x");
- break;
- case VM_PROT_READ | VM_PROT_WRITE:
- segmentNode.map["permissions"] = makeNode("rw-");
- break;
- case VM_PROT_READ | VM_PROT_EXECUTE:
- segmentNode.map["permissions"] = makeNode("r-x");
- break;
- default:
- fprintf(stderr, "Unknown permissions on segment '%.*s'\n", (int)info.segmentName.size(), info.segmentName.data());
- hasError = true;
- stop = true;
- }
-
- __block Node sectionsNode;
- ((const Header*)ma)->forEachSection(^(const Header::SectionInfo §Info, bool &stopSection) {
- if ( sectInfo.segmentName != info.segmentName )
- return;
-
- Node sectionNode;
- sectionNode.map["name"] = makeNode(std::string(sectInfo.sectionName));
- sectionNode.map["vmAddr"] = makeNode(hex(sectInfo.address));
- sectionNode.map["vmSize"] = makeNode(hex(sectInfo.size));
- sectionNode.map["vmEnd"] = makeNode(hex(sectInfo.address + sectInfo.size));
-
- sectionsNode.array.push_back(sectionNode);
- });
-
- if ( !sectionsNode.array.empty() ) {
- segmentNode.map["sections"] = sectionsNode;
- }
-
- segmentsNode.array.push_back(segmentNode);
- });
-
- Node dylibNode;
- dylibNode.map["name"] = makeNode(name);
- dylibNode.map["segments"] = segmentsNode;
-
- auto relativePathIt = relativePaths.find(name);
- if ( relativePathIt != relativePaths.end() )
- dylibNode.map["relativePath"] = makeNode(relativePathIt->second);
-
- dylibsNode.array.push_back(dylibNode);
- });
-
- topNode.map["dylibs"] = dylibsNode;
-
- printJSON(topNode, 0, std::cout);
- }
-
- if (options.printEntryPoint) {
- __block Node topNode;
-
- // add entry
- uint64_t entryOffset;
- bool usesCRT;
- Node entryPointNode;
- if ( ((const Header*)appCacheMA)->getEntry(entryOffset, usesCRT) ) {
- entryPointNode.value = hex(((const Header*)appCacheMA)->preferredLoadAddress() + entryOffset);
- }
-
- topNode.map["entrypoint"] = entryPointNode;
-
- printJSON(topNode, 0, std::cout);
- }
-
- if (options.printFixups) {
- __block Node topNode;
-
- __block uint64_t baseAddress = ~0ULL;
- ((const Header*)appCacheMA)->forEachSegment(^(const Header::SegmentInfo& info, bool& stop) {
- baseAddress = std::min(baseAddress, info.vmaddr);
- });
- uint64_t cacheBaseAddress = baseAddress;
- uint64_t textSegVMAddr = ((const Header*)appCacheMA)->preferredLoadAddress();
-
- auto getFixupsNode = [cacheBaseAddress, textSegVMAddr](const dyld3::MachOAnalyzer* ma) {
- __block Node fixupsNode;
-
- if (!ma->hasChainedFixups()) {
- return makeNode("none");
- }
-
- // Keep track of the fixups seen by chained fixups. The remainder might be
- // classic relocs if we are the x86_64 kernel collection
- __block std::set<uint64_t> seenFixupVMOffsets;
-
- __block Diagnostics diag;
- ma->withChainStarts(diag, 0, ^(const dyld_chained_starts_in_image* starts) {
- ma->forEachFixupInAllChains(diag, starts, false, ^(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc, const dyld_chained_starts_in_segment* segInfo, bool& stop) {
- uint64_t vmOffset = (uint8_t*)fixupLoc - (uint8_t*)ma;
- seenFixupVMOffsets.insert(vmOffset);
-
- // Correct for __DATA being before __TEXT, in which case the offset
- // is from __DATA, not a mach header offset
- vmOffset += (textSegVMAddr - cacheBaseAddress);
-
- fixupsNode.map[hex(vmOffset)] = makeNode("fixup");
- switch (segInfo->pointer_format) {
- case DYLD_CHAINED_PTR_64_KERNEL_CACHE:
- case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE: {
- uint64_t targetVMOffset = fixupLoc->kernel64.target;
- uint64_t targetVMAddr = targetVMOffset + cacheBaseAddress;
- std::string level = "kc(" + unpaddedDecimal(fixupLoc->kernel64.cacheLevel) + ")";
- std::string fixup = level + " + " + hex(targetVMAddr);
- if (fixupLoc->kernel64.isAuth) {
- fixup += " auth(";
- fixup += fixupLoc->kernel64.keyName();
- fixup += " ";
- fixup += fixupLoc->kernel64.addrDiv ? "addr" : "!addr";
- fixup += " ";
- fixup += unpaddedDecimal(fixupLoc->kernel64.diversity);
- fixup += ")";
- }
- fixupsNode.map[hex(vmOffset)] = makeNode(fixup);
- break;
- }
- default:
- diag.error("unknown pointer type %d", segInfo->pointer_format);
- break;
- }
- });
- });
- diag.assertNoError();
-
- ma->forEachRebase(diag, ^(const char *opcodeName, const dyld3::MachOAnalyzer::LinkEditInfo &leInfo,
- const Header::SegmentInfo *segments,
- bool segIndexSet, uint32_t pointerSize, uint8_t segmentIndex,
- uint64_t segmentOffset, dyld3::MachOAnalyzer::Rebase kind, bool &stop) {
- uint64_t rebaseVmAddr = segments[segmentIndex].vmaddr + segmentOffset;
- uint64_t runtimeOffset = rebaseVmAddr - textSegVMAddr;
- const uint8_t* fixupLoc = (const uint8_t*)ma + runtimeOffset;
-
- // Correct for __DATA being before __TEXT, in which case the offset
- // is from __DATA, not a mach header offset
- runtimeOffset += (textSegVMAddr - cacheBaseAddress);
-
- std::string fixup = "kc(0) + ";
- switch ( kind ) {
- case dyld3::MachOAnalyzer::Rebase::unknown:
- fixup += " : unhandled";
- break;
- case dyld3::MachOAnalyzer::Rebase::pointer32: {
- uint32_t value = *(uint32_t*)(fixupLoc);
- fixup += hex(value) + " : pointer32";
- break;
- }
- case dyld3::MachOAnalyzer::Rebase::pointer64: {
- uint64_t value = *(uint64_t*)(fixupLoc);
- fixup += hex(value) + " : pointer64";
- break;
- }
- case dyld3::MachOAnalyzer::Rebase::textPCrel32:
- fixup += " : pcrel32";
- break;
- case dyld3::MachOAnalyzer::Rebase::textAbsolute32:
- fixup += " : absolute32";
- break;
- }
- fixupsNode.map[hex(runtimeOffset)] = makeNode(fixup);
- });
- diag.assertNoError();
-
- return fixupsNode;
- };
-
- topNode.map["fixups"] = getFixupsNode(appCacheMA);
-
- __block Node dylibsNode;
- appCacheMA->forEachDylib(diag, ^(const MachOAnalyzer *ma, const char *name, bool &stop) {
- Node dylibNode;
- dylibNode.map["name"] = makeNode(name);
- dylibNode.map["fixups"] = getFixupsNode(ma);
-
- dylibsNode.array.push_back(dylibNode);
- });
-
- topNode.map["dylibs"] = dylibsNode;
-
- printJSON(topNode, 0, std::cout);
- }
-
- if (options.printSymbols) {
- __block Node topNode;
-
- __block Node dylibsNode;
- appCacheMA->forEachDylib(diag, ^(const MachOAnalyzer *ma, const char *name, bool &stop) {
- __block Node globalSymbolsNode;
- ma->forEachGlobalSymbol(diag, ^(const char *symbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool &stopSymbols) {
- Node symbolNode;
- symbolNode.map["name"] = makeNode(symbolName);
- symbolNode.map["vmAddr"] = makeNode(hex(n_value));
-
- globalSymbolsNode.array.push_back(symbolNode);
- });
-
- __block Node localSymbolsNode;
- ma->forEachLocalSymbol(diag, ^(const char *symbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool &stopSymbols) {
- Node symbolNode;
- symbolNode.map["name"] = makeNode(symbolName);
- symbolNode.map["vmAddr"] = makeNode(hex(n_value));
-
- localSymbolsNode.array.push_back(symbolNode);
- });
-
- if (globalSymbolsNode.array.empty())
- globalSymbolsNode = makeNode("none");
- if (localSymbolsNode.array.empty())
- localSymbolsNode = makeNode("none");
-
- Node dylibNode;
- dylibNode.map["name"] = makeNode(name);
- dylibNode.map["global-symbols"] = globalSymbolsNode;
- dylibNode.map["local-symbols"] = localSymbolsNode;
-
- dylibsNode.array.push_back(dylibNode);
- });
-
- topNode.map["dylibs"] = dylibsNode;
-
- printJSON(topNode, 0, std::cout);
- }
-
- if (options.printUUID) {
- __block Node topNode;
-
- // add uuid
- Node appUUIDNode;
- uuid_t appUUID = {};
- if ( ((Header*)appCacheMA)->getUuid(appUUID) ) {
- uuid_string_t uuidString;
- uuid_unparse_upper(appUUID, uuidString);
- appUUIDNode.value = uuidString;
- }
-
- topNode.map["uuid"] = appUUIDNode;
-
- auto getPlistUUID = ^(const char* jsonNodeName, CFStringRef keyName) {
- uuid_t uuid = {};
- const uint8_t* prelinkInfoBuffer = nullptr;
- uint64_t prelinkInfoBufferSize = 0;
- prelinkInfoBuffer = (const uint8_t*)appCacheMA->findSectionContent("__PRELINK_INFO", "__info", prelinkInfoBufferSize);
- if ( prelinkInfoBuffer != nullptr ) {
- CFReadStreamRef readStreamRef = CFReadStreamCreateWithBytesNoCopy(kCFAllocatorDefault, prelinkInfoBuffer, prelinkInfoBufferSize, kCFAllocatorNull);
- if ( !CFReadStreamOpen(readStreamRef) ) {
- fprintf(stderr, "Could not open plist stream\n");
- exit(1);
- }
- CFErrorRef errorRef = nullptr;
- CFPropertyListRef plistRef = CFPropertyListCreateWithStream(kCFAllocatorDefault, readStreamRef, prelinkInfoBufferSize, kCFPropertyListImmutable, nullptr, &errorRef);
- if ( errorRef != nullptr ) {
- CFStringRef stringRef = CFErrorCopyFailureReason(errorRef);
- fprintf(stderr, "Could not read plist because: %s\n", CFStringGetCStringPtr(stringRef, kCFStringEncodingASCII));
- CFRelease(stringRef);
- exit(1);
- }
- assert(CFGetTypeID(plistRef) == CFDictionaryGetTypeID());
- CFDataRef uuidDataRef = (CFDataRef)CFDictionaryGetValue((CFDictionaryRef)plistRef, keyName);
- if ( uuidDataRef != nullptr ) {
- CFDataGetBytes(uuidDataRef, CFRangeMake(0, CFDataGetLength(uuidDataRef)), uuid);
-
- Node uuidNode;
- uuid_string_t uuidString;
- uuid_unparse_upper(uuid, uuidString);
- uuidNode.value = uuidString;
- topNode.map[jsonNodeName] = uuidNode;
- }
- CFRelease(plistRef);
- CFRelease(readStreamRef);
- }
- };
-
- getPlistUUID("prelink-info-uuid", CFSTR("_PrelinkKCID"));
-
- // If we are an auxKC, then we should also have a reference to the baseKC UUID
- getPlistUUID("prelink-info-base-uuid", CFSTR("_BootKCID"));
-
- // If we are an pageableKC, then we should also have a reference to the pageableKC UUID
- getPlistUUID("prelink-info-pageable-uuid", CFSTR("_PageableKCID"));
-
- printJSON(topNode, 0, std::cout);
- }
-
- if (options.printPrelinkInfo) {
- __block Node topNode;
-
- const uint8_t* prelinkInfoBuffer = nullptr;
- uint64_t prelinkInfoBufferSize = 0;
- prelinkInfoBuffer = (const uint8_t*)appCacheMA->findSectionContent("__PRELINK_INFO", "__info", prelinkInfoBufferSize);
- if ( prelinkInfoBuffer != nullptr ) {
- CFReadStreamRef readStreamRef = CFReadStreamCreateWithBytesNoCopy(kCFAllocatorDefault, prelinkInfoBuffer, prelinkInfoBufferSize, kCFAllocatorNull);
- if ( !CFReadStreamOpen(readStreamRef) ) {
- fprintf(stderr, "Could not open plist stream\n");
- exit(1);
- }
- CFErrorRef errorRef = nullptr;
- CFPropertyListRef plistRef = CFPropertyListCreateWithStream(kCFAllocatorDefault, readStreamRef, prelinkInfoBufferSize, kCFPropertyListImmutable, nullptr, &errorRef);
- if ( errorRef != nullptr ) {
- CFStringRef stringRef = CFErrorCopyFailureReason(errorRef);
- fprintf(stderr, "Could not read plist because: %s\n", CFStringGetCStringPtr(stringRef, kCFStringEncodingASCII));
- CFRelease(stringRef);
- exit(1);
- }
- assert(CFGetTypeID(plistRef) == CFDictionaryGetTypeID());
- CFArrayRef kextPrelinkInfosRef = (CFArrayRef)CFDictionaryGetValue((CFDictionaryRef)plistRef, CFSTR("_PrelinkInfoDictionary"));
- if ( kextPrelinkInfosRef != nullptr ) {
- assert(CFGetTypeID(kextPrelinkInfosRef) == CFArrayGetTypeID());
-
- CFIndex count = CFArrayGetCount(kextPrelinkInfosRef);
- for ( CFIndex index = 0; index != count; ++index ) {
- CFDictionaryRef kextInfoRef = (CFDictionaryRef)CFArrayGetValueAtIndex(kextPrelinkInfosRef, index);
- assert(CFGetTypeID(kextInfoRef) == CFDictionaryGetTypeID());
-
- CFStringRef bundleIDRef = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)kextInfoRef, CFSTR("CFBundleIdentifier"));
- CFNumberRef executableSizeRef = (CFNumberRef)CFDictionaryGetValue((CFDictionaryRef)kextInfoRef, CFSTR("_PrelinkExecutableSize"));
-
- const char* bundleName = CFStringGetCStringPtr(bundleIDRef, kCFStringEncodingASCII);
- uint64_t execSize = 0;
- CFNumberGetValue(executableSizeRef, CFNumberGetType(executableSizeRef), &execSize);
-
- Node kextNode;
- kextNode.map["bundle-id"] = json::makeNode(bundleName);
- kextNode.map["executable-size"] = json::hex(execSize);
- topNode.array.push_back(std::move(kextNode));
- }
- }
- CFRelease(plistRef);
- CFRelease(readStreamRef);
- }
-
- printJSON(topNode, 0, std::cout);
- }
-
- if (options.printKModInfo) {
- __block Node topNode;
-
- appCacheMA->forEachDylib(diag, ^(const MachOAnalyzer *ma, const char *name, bool &stop) {
- Node dylibNode;
- dylibNode.map["name"] = makeNode(name);
-
- // Check for a global first
- __block uint64_t kmodInfoVMOffset = 0;
- __block bool found = false;
- {
- dyld3::MachOAnalyzer::FoundSymbol foundInfo;
- found = ma->findExportedSymbol(diag, "_kmod_info", true, foundInfo, nullptr);
- if ( found ) {
- kmodInfoVMOffset = foundInfo.value;
- }
- }
- // And fall back to a local if we need to
- if ( !found ) {
- ma->forEachLocalSymbol(diag, ^(const char* aSymbolName, uint64_t n_value, uint8_t n_type,
- uint8_t n_sect, uint16_t n_desc, bool& stopSymbols) {
- if ( strcmp(aSymbolName, "_kmod_info") == 0 ) {
- kmodInfoVMOffset = n_value - ((const Header*)ma)->preferredLoadAddress();
- found = true;
- stopSymbols = true;
- }
- });
- }
-
- if ( found ) {
- dyld3::MachOAppCache::KModInfo64_v1* kmodInfo = (dyld3::MachOAppCache::KModInfo64_v1*)((uint8_t*)ma + kmodInfoVMOffset);
- Node kmodInfoNode;
- kmodInfoNode.map["info-version"] = makeNode(unpaddedDecimal(kmodInfo->info_version));
- kmodInfoNode.map["name"] = makeNode((const char*)&kmodInfo->name[0]);
- kmodInfoNode.map["version"] = makeNode((const char*)&kmodInfo->version[0]);
- kmodInfoNode.map["address"] = makeNode(hex(kmodInfo->address));
- kmodInfoNode.map["size"] = makeNode(hex(kmodInfo->size));
-
- dylibNode.map["kmod_info"] = kmodInfoNode;
- } else {
- dylibNode.map["kmod_info"] = makeNode("none");
- }
-
- topNode.array.push_back(dylibNode);
- });
-
- printJSON(topNode, 0, std::cout);
- }
-
- if (options.printFIPS) {
- __block Node topNode;
-
- appCacheMA->forEachDylib(diag, ^(const MachOAnalyzer *ma, const char *name, bool &stop) {
- if ( strcmp(name, "com.apple.kec.corecrypto") != 0 )
- return;
-
- uint64_t hashStoreSize;
- const void* hashStoreLocation = ma->findSectionContent("__TEXT", "__fips_hmacs", hashStoreSize);
- assert(hashStoreLocation != nullptr);
-
- const uint8_t* hashBuffer = (const uint8_t*)hashStoreLocation;
- std::string hashString;
-
- for (int i = 0; i < hashStoreSize; ++i) {
- uint8_t byte = hashBuffer[i];
- uint8_t nibbleL = byte & 0x0F;
- uint8_t nibbleH = byte >> 4;
- if ( nibbleH < 10 ) {
- hashString += '0' + nibbleH;
- } else {
- hashString += 'a' + (nibbleH-10);
- }
- if ( nibbleL < 10 ) {
- hashString += '0' + nibbleL;
- } else {
- hashString += 'a' + (nibbleL-10);
- }
- }
-
- stop = true;
-
- topNode.map["fips"] = makeNode(hashString);
- });
-
- printJSON(topNode, 0, std::cout);
- }
-
- if (options.printFunctionStarts) {
- __block Node topNode;
-
- auto getStartsNode = [](const dyld3::MachOAnalyzer* ma) {
- __block Node functionStartsNode;
-
- uint64_t loadAddress = ((const Header*)ma)->preferredLoadAddress();
- ma->forEachFunctionStart(^(uint64_t runtimeOffset) {
- Node functionStart = makeNode(hex(loadAddress + runtimeOffset));
- functionStartsNode.array.push_back(functionStart);
- });
-
- return functionStartsNode;
- };
-
- topNode.map["function-starts"] = getStartsNode(appCacheMA);
-
- __block Node dylibsNode;
- appCacheMA->forEachDylib(diag, ^(const MachOAnalyzer *ma, const char *name, bool &stop) {
- Node dylibNode;
- dylibNode.map["name"] = makeNode(name);
- dylibNode.map["function-starts"] = getStartsNode(ma);
-
- dylibsNode.array.push_back(dylibNode);
- });
-
- topNode.map["dylibs"] = dylibsNode;
-
- printJSON(topNode, 0, std::cout);
- }
-
- return 0;
-}
-
-static int validateFile(const ValidateOptions& options) {
- // Verify any required options
- if (gOpts.archs.size() != 1)
- exit_usage("-arch");
- if (gOpts.platform == nullptr)
- exit_usage("-platform");
- if (options.filePath == nullptr)
- exit_usage();
-
- const GradedArchitectures& archs = GradedArchitectures::forName(gOpts.archs[0], /*keysOff=*/false, /*isKernel=*/true);
- Platform platform = Platform();
-
- // HACK: Pass a real option for building a kernel app cache
- if (strcmp(gOpts.platform, "kernel")) {
- fprintf(stderr, "Could not create app cache because: unknown platform '%s'\n", gOpts.platform);
- return 1;
- }
-
- __block Diagnostics diag;
-
- std::string file = options.filePath;
- {
- FileSystemPhysical fileSystem;
- char fileRealPath[MAXPATHLEN];
- LoadedFileInfo loadedFileInfo = MachOAnalyzer::load(diag, fileSystem, file.c_str(), archs, platform, fileRealPath);
- if (diag.hasError()) {
- fprintf(stderr, "Could not load file '%s' because: %s\n", file.c_str(), diag.errorMessage().c_str());
- diag.clearError();
- return 1;
- }
-
- MachOAnalyzer* ma = (MachOAnalyzer*)loadedFileInfo.fileContent;
- if (ma == nullptr) {
- fprintf(stderr, "Could not load file: %s\n", file.c_str());
- return 1;
- }
-
- auto errorHandler = ^(const char* msg) {
- diag.warning("File '%s' cannot be placed in kernel collection because: %s", file.c_str(), msg);
- };
- if (ma->canBePlacedInKernelCollection(file.c_str(), errorHandler)) {
- return 0;
- } else {
- fileSystem.unloadFile(loadedFileInfo);
- }
- }
-
- {
- // Since we found no files, print warnings for the ones we tried
- if (diag.warnings().empty()) {
- fprintf(stderr, "File '%s' was not valid for app-cache\n", file.c_str());
- } else {
- for (const std::string& msg : diag.warnings()) {
- fprintf(stderr, " %s\n", msg.c_str());
- }
- }
- return 1;
- }
-
- return 0;
-}
-
-static void forEachBundle(const char* bundlesDirectoryPath,
- void (^callback)(CFBundleRef bundleRef, const char* bundleName)) {
- CFStringRef sourcePath = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, bundlesDirectoryPath,
- kCFStringEncodingASCII, kCFAllocatorNull);
- CFURLRef sourceURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, sourcePath,
- kCFURLPOSIXPathStyle, true);
- CFArrayRef bundles = CFBundleCreateBundlesFromDirectory(kCFAllocatorDefault, sourceURL, nullptr);
-
- for (CFIndex i = 0, e = CFArrayGetCount(bundles); i != e; ++i) {
- CFBundleRef bundleRef = (CFBundleRef)CFArrayGetValueAtIndex(bundles, i);
- CFStringRef bundleID = CFBundleGetIdentifier(bundleRef);
- if (!bundleID)
- continue;
- const char* bundleName = CFStringGetCStringPtr(bundleID, kCFStringEncodingASCII);
- callback(bundleRef, bundleName);
- }
-
- CFRelease(sourcePath);
- CFRelease(sourceURL);
- CFRelease(bundles);
-}
-
-static int listBundles(const ListBundlesOptions& options) {
- // Verify any required options
- if (options.directoryPath == nullptr)
- exit_usage();
-
- forEachBundle(options.directoryPath, ^(CFBundleRef bundleRef, const char* bundleName){
- printf("Bundle: %s\n", bundleName);
- });
-
- return 0;
-}
-
-static CFDataRef
-createKernelCollectionForArch(const CreateKernelCollectionOptions& options, const char* arch,
- Diagnostics& diag) {
- const GradedArchitectures& archs = GradedArchitectures::forName(arch, /*keysOff=*/false, /*isKernel=*/true);
- Platform platform = Platform();
-
-
- KernelCollectionBuilder* kcb = nullptr;
- {
- CFStringRef archStringRef = CFStringCreateWithCString(kCFAllocatorDefault, arch, kCFStringEncodingASCII);
- BuildOptions_v1 buildOptions = { 1, options.collectionKind, options.stripMode, archStringRef, options.verbose };
- kcb = createKernelCollectionBuilder(&buildOptions);
- CFRelease(archStringRef);
- }
-
- FileSystemPhysical fileSystem;
-
- auto loadKernelCollection = ^(const char* kernelCollectionPath, CollectionKind collectionKind) {
- if (!fileSystem.fileExists(kernelCollectionPath)) {
- fprintf(stderr, "kernel collection path does not exist: %s\n", options.kernelPath);
- return false;
- }
- LoadedFileInfo info;
- char realerPath[MAXPATHLEN];
- void (^fileErrorLog)(const char *format, ...) __printflike(1, 2)
- = ^(const char *format, ...) __printflike(1, 2) {
- va_list list;
- va_start(list, format);
- diag.error(format, va_list_wrap(list));
- va_end(list);
- };
- bool loadedFile = fileSystem.loadFile(kernelCollectionPath, info, realerPath, fileErrorLog);
- if ( !loadedFile )
- return false;
- CFStringRef pathStringRef = CFStringCreateWithCString(kCFAllocatorDefault, kernelCollectionPath, kCFStringEncodingASCII);
- CFDataRef dataRef = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const uint8_t*)info.fileContent, info.fileContentLen, kCFAllocatorNull);
- if ( !addCollectionFile(kcb, pathStringRef, dataRef, collectionKind) ) {
- diag.error("Could not load kernel collection file");
- return false;
- }
- CFRelease(dataRef);
- CFRelease(pathStringRef);
- return true;
- };
-
- switch (options.collectionKind) {
- case unknownKC:
- fprintf(stderr, "Invalid kernel collection kind\n");
- exit(1);
- case baseKC: {
- if (!fileSystem.fileExists(options.kernelPath)) {
- fprintf(stderr, "Kernel path does not exist: %s\n", options.kernelPath);
- return {};
- }
- LoadedFileInfo info;
- char realerPath[MAXPATHLEN];
- void (^fileErrorLog)(const char *format, ...) __printflike(1, 2)
- = ^(const char *format, ...) __printflike(1, 2) {
- va_list list;
- va_start(list, format);
- diag.error(format, va_list_wrap(list));
- va_end(list);
- };
- bool loadedFile = fileSystem.loadFile(options.kernelPath, info, realerPath, fileErrorLog);
- if ( !loadedFile )
- return {};
- CFStringRef pathStringRef = CFStringCreateWithCString(kCFAllocatorDefault, options.kernelPath, kCFStringEncodingASCII);
- CFDataRef dataRef = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const uint8_t*)info.fileContent, info.fileContentLen, kCFAllocatorNull);
- if ( !addKernelFile(kcb, pathStringRef, dataRef) ) {
- uint64_t errorCount = 0;
- const char* const* errors = getErrors(kcb, &errorCount);
- for (uint64_t i = 0; i != errorCount; ++i)
- diag.error("Could not load kernel file because: '%s'", errors[i]);
- return {};
- }
- CFRelease(dataRef);
- CFRelease(pathStringRef);
- break;
- }
- case pageableKC:
- if ( !loadKernelCollection(options.kernelCollectionPath, baseKC) )
- return {};
- break;
- case auxKC:
- if ( !loadKernelCollection(options.kernelCollectionPath, baseKC) )
- return {};
- // Pageable is optional
- if ( options.pageableCollectionPath != nullptr ) {
- if ( !loadKernelCollection(options.pageableCollectionPath, pageableKC) )
- return {};
- }
- break;
- }
-
- if ( !options.bundleIDs.empty() ) {
- struct BundleData {
- std::string executablePath;
- std::string bundlePath;
- std::vector<std::string> dependencies;
- CFDictionaryRef infoPlist = nullptr;
- };
- __block std::map<std::string, BundleData> foundBundles;
-
- // Look for bundles in the extensions directory and any PlugIns directories its kext's contain
- __block std::list<std::pair<std::string, bool>> kextDirectoriesToProcess;
- kextDirectoriesToProcess.push_back({ options.extensionsPath, true });
- while ( !kextDirectoriesToProcess.empty() ) {
- std::string kextDir = kextDirectoriesToProcess.front().first;
- bool lookForPlugins = kextDirectoriesToProcess.front().second;
- kextDirectoriesToProcess.pop_front();
-
- __block bool foundError = false;
- forEachBundle(kextDir.c_str(), ^(CFBundleRef bundleRef, const char* bundleName) {
-
- if (foundError)
- return;
-
- // If the directory contains a PlugIns directory, then add it to the list to seach for kexts
- if (lookForPlugins) {
- CFURLRef pluginsRelativeURL = CFBundleCopyBuiltInPlugInsURL(bundleRef);
- if ( pluginsRelativeURL != nullptr ) {
- CFURLRef pluginsAbsoluteURL = CFURLCopyAbsoluteURL(pluginsRelativeURL);
- CFStringRef pluginString = CFURLCopyFileSystemPath(pluginsAbsoluteURL, kCFURLPOSIXPathStyle);
- const char* pluginPath = CFStringGetCStringPtr(pluginString, kCFStringEncodingASCII);
- kextDirectoriesToProcess.push_back({ pluginPath, false });
-
- CFRelease(pluginString);
- CFRelease(pluginsAbsoluteURL);
- CFRelease(pluginsRelativeURL);
- }
- }
-
-#if 0
- // For now always load bundles as we don't require every bundle to be listed on the command line
- // but can instead bring them in on demand.
-
- // Once we've looked for plugins, if we don't want this bundle then we can skip validating it.
- if ( foundBundles.count(bundleName) == 0 )
- return;
-#endif
-
- BundleData bundleData;
- bundleData.infoPlist = CFBundleGetInfoDictionary(bundleRef);
-
- CFURLRef bundleExecutableRelativeURL = CFBundleCopyExecutableURL(bundleRef);
-
- // Its ok to be missing an executable. We'll just skip this bundle
- if ( bundleExecutableRelativeURL == nullptr ) {
- // FIXME: Its possibly not ok to be missing the executable if its actually listed
- // as a CFBundleExecutable path in the plist
- foundBundles[bundleName] = bundleData;
- return;
- }
-
- CFURLRef bundleExecutableAbsoluteURL = CFURLCopyAbsoluteURL(bundleExecutableRelativeURL);
- CFStringRef bundleExecutableString = CFURLCopyFileSystemPath(bundleExecutableAbsoluteURL, kCFURLPOSIXPathStyle);
- const char* bundleExecutablePath = CFStringGetCStringPtr(bundleExecutableString, kCFStringEncodingASCII);
-
- // Check for an arch specific dependency list first
- std::string archBundleLibraries = std::string("OSBundleLibraries") + "_" + arch;
- CFStringRef archBundleLibrariesStringRef = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, archBundleLibraries.c_str(),
- kCFStringEncodingASCII, kCFAllocatorNull);
- CFTypeRef depsRef = CFBundleGetValueForInfoDictionaryKey(bundleRef, archBundleLibrariesStringRef);
- if ( depsRef == nullptr ) {
- // No arch specific deps, so try the defaults
- depsRef = CFBundleGetValueForInfoDictionaryKey(bundleRef, CFSTR("OSBundleLibraries"));
- }
- if (depsRef != nullptr) {
- if (CFGetTypeID(depsRef) != CFDictionaryGetTypeID()) {
- fprintf(stderr, "Bad bundle '%s' (\"OSBundleLibraries\" is not a dictionary)\n", bundleName);
- foundError = true;
- return;
- }
-
- CFDictionaryRef dictRef = (CFDictionaryRef)depsRef;
- CFDictionaryApplyFunction(dictRef, [](const void *key, const void *value, void *context) {
- BundleData* bundleData = (BundleData*)context;
- CFStringRef keyRef = (CFStringRef)key;
- //CFStringRef valueRef = (CFStringRef)value;
- bundleData->dependencies.push_back(CFStringGetCStringPtr(keyRef, kCFStringEncodingASCII));
- }, &bundleData);
- }
-
- // Make sure no-one tries to link the kernel directly. They must do so via symbol sets
- if ( !bundleData.dependencies.empty() ) {
- for (const std::string& dep : bundleData.dependencies) {
- if (dep == "com.apple.kernel") {
- fprintf(stderr, "Rejecting bundle '%s' as it is trying to link directly to the kernel\n",
- bundleName);
- foundError = true;
- return;
- }
- }
- }
-
- bundleData.executablePath = bundleExecutablePath;
-
- CFURLRef bundleURLRef = CFBundleCopyBundleURL(bundleRef);
- CFStringRef bundleURLString = CFURLCopyFileSystemPath(bundleURLRef, kCFURLPOSIXPathStyle);
- const char* bundleURLPath = CFStringGetCStringPtr(bundleURLString, kCFStringEncodingASCII);
- if (strncmp(bundleURLPath, options.extensionsPath, strlen(options.extensionsPath)) != 0) {
- fprintf(stderr, "Bundle path '%s' is not within extensions directory '%s'\n",
- bundleURLPath, options.extensionsPath);
- }
- // Don't remove the whole extensions prefix, but instead the volume root, if we have one
- bundleData.bundlePath = bundleURLPath + strlen(options.volumeRoot);
- foundBundles[bundleName] = bundleData;
-
- CFRelease(bundleExecutableString);
- CFRelease(bundleExecutableAbsoluteURL);
- CFRelease(bundleExecutableRelativeURL);
- });
-
- if (foundError)
- return {};
- }
-
- __block std::set<std::string> existingBundles;
- auto addSymbolSetsBundleIDs = ^(const dyld3::MachOAnalyzer* kernelMA) {
- assert(kernelMA != nullptr);
-
- __block std::list<std::string> nonASCIIStrings;
- auto getString = ^(Diagnostics& diags, CFStringRef symbolNameRef) {
- const char* symbolName = CFStringGetCStringPtr(symbolNameRef, kCFStringEncodingUTF8);
- if ( symbolName != nullptr )
- return symbolName;
-
- CFIndex len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(symbolNameRef), kCFStringEncodingUTF8);
- char buffer[len + 1];
- if ( !CFStringGetCString(symbolNameRef, buffer, len, kCFStringEncodingUTF8) ) {
- diags.error("Could not convert string to ASCII");
- return (const char*)nullptr;
- }
- buffer[len] = '\0';
- nonASCIIStrings.push_back(buffer);
- return nonASCIIStrings.back().c_str();
- };
-
- uint64_t symbolSetsSize = 0;
- const void* symbolSetsContent = kernelMA->findSectionContent("__LINKINFO", "__symbolsets", symbolSetsSize);
- if ( symbolSetsContent != nullptr ) {
-
- // A helper to automatically call CFRelease when we go out of scope
- struct AutoReleaseTypeRef {
- AutoReleaseTypeRef() = default;
- ~AutoReleaseTypeRef() {
- if ( ref != nullptr ) {
- CFRelease(ref);
- }
- }
- void setRef(CFTypeRef typeRef) {
- assert(ref == nullptr);
- ref = typeRef;
- }
-
- CFTypeRef ref = nullptr;
- };
-
- AutoReleaseTypeRef dataRefReleaser;
- AutoReleaseTypeRef plistRefReleaser;
-
- CFDataRef dataRef = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const uint8_t*)symbolSetsContent, symbolSetsSize, kCFAllocatorNull);
- if ( dataRef == nullptr ) {
- diag.error("Could not create data ref for symbol sets");
- return false;
- }
- dataRefReleaser.setRef(dataRef);
-
- CFErrorRef errorRef = nullptr;
- CFPropertyListRef plistRef = CFPropertyListCreateWithData(kCFAllocatorDefault, dataRef, kCFPropertyListImmutable, nullptr, &errorRef);
- if (errorRef != nullptr) {
- CFStringRef errorString = CFErrorCopyDescription(errorRef);
- diag.error("Could not load plist because :%s",
- CFStringGetCStringPtr(errorString, kCFStringEncodingASCII));
- CFRelease(errorRef);
- return false;
- }
- if ( plistRef == nullptr ) {
- diag.error("Could not create plist ref for symbol sets");
- return false;
- }
- plistRefReleaser.setRef(plistRef);
-
- if ( CFGetTypeID(plistRef) != CFDictionaryGetTypeID() ) {
- diag.error("Symbol set plist should be a dictionary");
- return false;
- }
- CFDictionaryRef symbolSetsDictRef = (CFDictionaryRef)plistRef;
- CFArrayRef symbolSetArrayRef = (CFArrayRef)CFDictionaryGetValue(symbolSetsDictRef, CFSTR("SymbolsSets"));
- if ( symbolSetArrayRef != nullptr ) {
- if ( CFGetTypeID(symbolSetArrayRef) != CFArrayGetTypeID() ) {
- diag.error("SymbolsSets value should be an array");
- return false;
- }
- for (CFIndex symbolSetIndex = 0; symbolSetIndex != CFArrayGetCount(symbolSetArrayRef); ++symbolSetIndex) {
- CFDictionaryRef symbolSetDictRef = (CFDictionaryRef)CFArrayGetValueAtIndex(symbolSetArrayRef, symbolSetIndex);
- if ( CFGetTypeID(symbolSetDictRef) != CFDictionaryGetTypeID() ) {
- diag.error("Symbol set element should be a dictionary");
- return false;
- }
-
- // CFBundleIdentifier
- CFStringRef bundleIDRef = (CFStringRef)CFDictionaryGetValue(symbolSetDictRef, CFSTR("CFBundleIdentifier"));
- if ( (bundleIDRef == nullptr) || (CFGetTypeID(bundleIDRef) != CFStringGetTypeID()) ) {
- diag.error("Symbol set bundle ID should be a string");
- return false;
- }
-
- const char* dylibID = getString(diag, bundleIDRef);
- if ( dylibID == nullptr )
- return false;
- existingBundles.insert(dylibID);
- }
- }
- }
- return true;
- };
-
- auto addExistingBundleIDs = ^(const char* path, bool isBaseKC) {
- char fileRealPath[MAXPATHLEN];
- auto kernelCollectionLoadedFileInfo = MachOAnalyzer::load(diag, fileSystem, path, archs, platform, fileRealPath);
- if ( diag.hasError() ) {
- fprintf(stderr, "Could not load file '%s' because: %s\n", path, diag.errorMessage().c_str());
- return false;
- }
- const MachOAppCache* appCacheMA = (const MachOAppCache*)kernelCollectionLoadedFileInfo.fileContent;
- if (appCacheMA == nullptr) {
- fprintf(stderr, "Could not load file: %s\n", path);
- return false;
- }
- if ( !appCacheMA->isFileSet() ) {
- fprintf(stderr, "kernel collection is not a cache file: %s\n", path);
- return false;
- }
- __block const dyld3::MachOAnalyzer* kernelMA = nullptr;
- appCacheMA->forEachDylib(diag, ^(const MachOAnalyzer *ma, const char *name, bool &stop) {
- if ( ma->isStaticExecutable() ) {
- kernelMA = ma;
- }
- existingBundles.insert(name);
- });
-
- if ( isBaseKC ) {
- if ( !addSymbolSetsBundleIDs(kernelMA) )
- return false;
- }
- fileSystem.unloadFile(kernelCollectionLoadedFileInfo);
- return true;
- };
- if ( options.collectionKind == baseKC ) {
- char fileRealPath[MAXPATHLEN];
- auto kernelLoadedFileInfo = MachOAnalyzer::load(diag, fileSystem, options.kernelPath, archs, platform, fileRealPath);
- if ( diag.hasError() ) {
- fprintf(stderr, "Could not load file '%s' because: %s\n", options.kernelPath, diag.errorMessage().c_str());
- return {};
- }
- const MachOAppCache* kernelMA = (const MachOAppCache*)kernelLoadedFileInfo.fileContent;
- if (kernelMA == nullptr) {
- fprintf(stderr, "Could not load file: %s\n", options.kernelPath);
- return {};
- }
- if ( !kernelMA->isStaticExecutable() ) {
- fprintf(stderr, "kernel is not a static executable: %s\n", options.kernelPath);
- return {};
- }
-
- if ( !addSymbolSetsBundleIDs(kernelMA) )
- return {};
- fileSystem.unloadFile(kernelLoadedFileInfo);
- }
- if ( (options.collectionKind == auxKC) || (options.collectionKind == pageableKC) ) {
- // Work out which bundle-ids are already in the base KC
- if ( !addExistingBundleIDs(options.kernelCollectionPath, true) )
- return {};
- }
- if ( options.pageableCollectionPath != nullptr ) {
- // Work out which bundle-ids are already in the pageable KC
- if ( !addExistingBundleIDs(options.pageableCollectionPath, false) )
- return {};
- }
-
- std::set<std::string> processedBundleIDs;
- std::list<const char*> bundleIDsToLoad;
- bundleIDsToLoad.insert(bundleIDsToLoad.end(), options.bundleIDs.begin(), options.bundleIDs.end());
- while (!bundleIDsToLoad.empty()) {
- std::string bundleID = bundleIDsToLoad.front();
- bundleIDsToLoad.pop_front();
-
- std::string stripModeString;
- if ( const char* colonPos = strchr(bundleID.c_str(), ':') ) {
- stripModeString = colonPos + 1;
- bundleID.erase(colonPos - bundleID.data());
- }
-
- // If we've seen this one already then skip it
- if (!processedBundleIDs.insert(bundleID).second)
- continue;
-
- // Find the bundle for this ID
- auto it = foundBundles.find(bundleID);
- if (it == foundBundles.end()) {
- fprintf(stderr, "[WARNING]: Could not find bundle with ID '%s'\n", bundleID.c_str());
- continue;
- }
-
- BundleData& bundleData = it->second;
-
- LoadedFileInfo info;
-
- // Codeless kexts don't have an executable path, but we still want to put their
- // plist in the prelink info
- bool isCodeless = bundleData.executablePath.empty();
- if ( !isCodeless ) {
- char realerPath[MAXPATHLEN];
- void (^fileErrorLog)(const char *format, ...) __printflike(1, 2)
- = ^(const char *format, ...) __printflike(1, 2) {
- va_list list;
- va_start(list, format);
- diag.error(format, va_list_wrap(list));
- va_end(list);
- };
- bool loadedFile = fileSystem.loadFile(bundleData.executablePath.c_str(), info, realerPath, fileErrorLog);
- if ( !loadedFile )
- return {};
- }
-
- std::vector<const char*> deps;
- for (const std::string& dependency : bundleData.dependencies)
- deps.push_back(dependency.c_str());
-
- CFStringRef kextPathStringRef = nullptr;
- CFDataRef kextDataRef = nullptr;
- if ( !isCodeless) {
- kextPathStringRef = CFStringCreateWithCString(kCFAllocatorDefault, bundleData.executablePath.c_str(), kCFStringEncodingASCII);
- kextDataRef = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const uint8_t*)info.fileContent, info.fileContentLen, kCFAllocatorNull);
- }
-
- CFMutableArrayRef kextDepsArrayRef = CFArrayCreateMutable(kCFAllocatorDefault, bundleData.dependencies.size(), &kCFTypeArrayCallBacks);
- for (const std::string& dependency : bundleData.dependencies) {
- CFStringRef depStringRef = CFStringCreateWithCString(kCFAllocatorDefault, dependency.c_str(), kCFStringEncodingASCII);
- CFArrayAppendValue(kextDepsArrayRef, depStringRef);
- CFRelease(depStringRef);
- }
- CFStringRef kextBundleIDStringRef = CFStringCreateWithCString(kCFAllocatorDefault, bundleID.c_str(), kCFStringEncodingASCII);
- CFStringRef kextBundlePathStringRef = CFStringCreateWithCString(kCFAllocatorDefault, bundleData.bundlePath.c_str(), kCFStringEncodingASCII);
-
- BinaryStripMode stripMode = binaryStripNone;
- if ( !stripModeString.empty() ) {
- if ( stripModeString == "locals" ) {
- stripMode = binaryStripLocals;
- } else if ( stripModeString == "exports" ) {
- stripMode = binaryStripExports;
- } else if ( stripModeString == "all" ) {
- stripMode = binaryStripAll;
- } else {
- diag.error("Unknown strip mode: '%s'", stripModeString.c_str());
- return {};
- }
- }
-
- KextFileData_v1 fileData = { 1, kextPathStringRef, kextDataRef,
- kextDepsArrayRef, kextBundleIDStringRef, kextBundlePathStringRef,
- bundleData.infoPlist, stripMode };
-
- if ( !addKextDataFile(kcb, &fileData) ) {
- uint64_t errorCount = 0;
- const char* const* errors = getErrors(kcb, &errorCount);
- for (uint64_t i = 0; i != errorCount; ++i)
- diag.error("Could not load kext file because: '%s'", errors[i]);
- return {};
- }
-
- // Walk the dependencies and add any new ones to the list
- for (const std::string& dependency : bundleData.dependencies) {
- if ( existingBundles.find(dependency) == existingBundles.end() )
- bundleIDsToLoad.push_back(dependency.c_str());
- }
- }
-
- // Filter dependencies to kext's with binaries
-#if 0
- const std::map<std::string, BundleData>* foundBundlesPtr = &foundBundles;
- for (AppCacheBuilder::InputDylib& file : loadedFiles) {
- file.dylibDeps.erase(std::remove_if(file.dylibDeps.begin(), file.dylibDeps.end(),
- [&](const std::string& depName) {
- auto it = foundBundlesPtr->find(depName);
- assert(it != foundBundlesPtr->end());
- return it->second.executablePath.empty();
- }),file.dylibDeps.end());
- }
-#endif
- }
-
-#if 0
- for (AppCacheBuilder::InputDylib& file : loadedFiles) {
- char fileRealPath[MAXPATHLEN];
- const char* path = file.dylib.loadedFileInfo.path;
- LoadedFileInfo loadedFileInfo = MachOAnalyzer::load(diag, fileSystem, path, archs, platform, fileRealPath);
- if ( diag.hasError() ) {
- fprintf(stderr, "Could not load file '%s' because: %s\n", path, diag.errorMessage().c_str());
- return {};
- }
-
- MachOAnalyzer* ma = (MachOAnalyzer*)loadedFileInfo.fileContent;
- if (ma == nullptr) {
- fprintf(stderr, "Could not load file: %s\n", path);
- return {};
- }
-
- auto errorHandler = ^(const char* msg) {
- diag.error("Binary located at '%s' cannot be placed in kernel collection because: %s", path, msg);
- };
- if (ma->canBePlacedInKernelCollection(path, errorHandler)) {
- DyldSharedCache::MappedMachO mappedFile(path, ma, loadedFileInfo.sliceLen, false, false,
- loadedFileInfo.sliceOffset, loadedFileInfo.mtime,
- loadedFileInfo.inode);
- CacheBuilder::LoadedMachO loadedMachO = { mappedFile, loadedFileInfo, nullptr };
- file.dylib = loadedMachO;
- } else {
- fileSystem.unloadFile(loadedFileInfo);
- }
- if ( diag.hasError() ) {
- fprintf(stderr, "%s\n", diag.errorMessage().c_str());
- return {};
- }
- }
-#endif
-
-#if 0
- if (loadedFiles.empty()) {
- fprintf(stderr, "Could not find any valid files to create kernel collection\n");
-
- // Since we found no files, print warnings for the ones we tried
- if (!diag.warnings().empty()) {
- fprintf(stderr, "Failed to use the following files:\n");
- for (const std::string& msg : diag.warnings()) {
- fprintf(stderr, " %s\n", msg.c_str());
- }
- }
- return {};
- }
-
- if (options.verbose) {
- for (const AppCacheBuilder::InputDylib& loadedFile : loadedFiles)
- fprintf(stderr, "Building cache with file: %s\n", loadedFile.dylib.loadedFileInfo.path);
- }
-#endif
-
- for (const SectionData& sectData : options.sections) {
- CFStringRef segmentName = CFStringCreateWithCString(kCFAllocatorDefault, sectData.segmentName, kCFStringEncodingASCII);
- CFStringRef sectionName = nullptr;
- if ( sectData.sectionName != nullptr )
- sectionName = CFStringCreateWithCString(kCFAllocatorDefault, sectData.sectionName, kCFStringEncodingASCII);
-
- CFDataRef sectionData = nullptr;
- {
- struct stat stat_buf;
- int fd = ::open(sectData.payloadFilePath, O_RDONLY, 0);
- if (fd == -1) {
- diag.error("can't open file '%s', errno=%d\n", sectData.payloadFilePath, errno);
- return {};
- }
-
- if (fstat(fd, &stat_buf) == -1) {
- diag.error("can't stat open file '%s', errno=%d\n", sectData.payloadFilePath, errno);
- ::close(fd);
- return {};
- }
-
- const void* buffer = mmap(NULL, (size_t)stat_buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
- if (buffer == MAP_FAILED) {
- diag.error("mmap() for file at %s failed, errno=%d\n", sectData.payloadFilePath, errno);
- ::close(fd);
- return {};
- }
- ::close(fd);
-
- sectionData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const uint8_t*)buffer, stat_buf.st_size, kCFAllocatorNull);
- }
-
- if ( !addSegmentData(kcb, segmentName, sectionName, sectionData) ) {
- uint64_t errorCount = 0;
- const char* const* errors = getErrors(kcb, &errorCount);
- for (uint64_t i = 0; i != errorCount; ++i)
- diag.error("Could not load section data file because: '%s'", errors[i]);
- return {};
- }
- }
-
- if ( options.prelinkInfoExtraData != nullptr ) {
- struct stat stat_buf;
- int fd = ::open(options.prelinkInfoExtraData, O_RDONLY, 0);
- if (fd == -1) {
- diag.error("can't open file '%s', errno=%d\n", options.prelinkInfoExtraData, errno);
- return {};
- }
-
- if (fstat(fd, &stat_buf) == -1) {
- diag.error("can't stat open file '%s', errno=%d\n", options.prelinkInfoExtraData, errno);
- ::close(fd);
- return {};
- }
-
- const void* buffer = mmap(NULL, (size_t)stat_buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
- if (buffer == MAP_FAILED) {
- diag.error("mmap() for file at %s failed, errno=%d\n", options.prelinkInfoExtraData, errno);
- ::close(fd);
- return {};
- }
- ::close(fd);
-
- CFDataRef prelinkInfoData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const uint8_t*)buffer, stat_buf.st_size, kCFAllocatorNull);
-
- CFErrorRef errorRef = nullptr;
- CFPropertyListRef plistRef = CFPropertyListCreateWithData(kCFAllocatorDefault, prelinkInfoData, kCFPropertyListImmutable, nullptr, &errorRef);
- if (errorRef != nullptr) {
- CFStringRef errorString = CFErrorCopyDescription(errorRef);
- diag.error("Could not load prelink info plist because :%s",
- CFStringGetCStringPtr(errorString, kCFStringEncodingASCII));
- CFRelease(errorRef);
- return {};
- }
- if ( plistRef == nullptr ) {
- diag.error("Could not create plist ref for prelink info");
- return {};
- }
- if ( CFGetTypeID(plistRef) != CFDictionaryGetTypeID() ) {
- diag.error("Prelink info plist should be a dictionary");
- return {};
- }
-
- if ( !addPrelinkInfo(kcb, (CFDictionaryRef)plistRef) ) {
- uint64_t errorCount = 0;
- const char* const* errors = getErrors(kcb, &errorCount);
- for (uint64_t i = 0; i != errorCount; ++i)
- diag.error("Could not prelink data file because: '%s'", errors[i]);
- return {};
- }
- }
-
- bool success = runKernelCollectionBuilder(kcb);
- uint64_t errorCount = 0;
- const char* const* errors = getErrors(kcb, &errorCount);
- if ( errors != nullptr ) {
- if ( !options.printJSONErrors ) {
- for (uint64_t i = 0; i != errorCount; ++i) {
- fprintf(stderr, "Could not build kernel collection because '%s'\n", errors[i]);
- }
- }
- CFDictionaryRef errorDictRef = getKextErrors(kcb);
- if ( errorDictRef != nullptr ) {
- Node rootNode;
-
- CFDictionaryApplyFunction(errorDictRef, [](const void *key, const void *value, void *context) {
- Node* rootNode = (Node*)context;
- CFStringRef keyRef = (CFStringRef)key;
- CFArrayRef valueRef = (CFArrayRef)value;
-
- Node bundleNode;
- bundleNode.map["id"] = Node(CFStringGetCStringPtr(keyRef, kCFStringEncodingASCII));
-
- Node errorsNode;
- CFArrayApplyFunction(valueRef, CFRangeMake(0, CFArrayGetCount(valueRef)), [](const void *value, void *context) {
- Node* errorsNode = (Node*)context;
- CFStringRef valueRef = (CFStringRef)value;
-
- errorsNode->array.push_back(Node(CFStringGetCStringPtr(valueRef, kCFStringEncodingASCII)));
- }, &errorsNode);
-
- bundleNode.map["errors"] = errorsNode;
-
- rootNode->array.push_back(bundleNode);
- }, &rootNode);
-
- // sort the nodes so that the output is reproducible
- std::sort(rootNode.array.begin(), rootNode.array.end(),
- [](const Node& a, const Node&b) {
- return a.map.find("id")->second.value < b.map.find("id")->second.value;
- });
-
- printJSON(rootNode);
- } else {
- Node rootNode;
- for (uint64_t i = 0; i != errorCount; ++i) {
- rootNode.array.push_back(Node(errors[i]));
- }
- printJSON(rootNode);
- }
- return {};
- }
-
- if ( !success )
- return {};
-
- uint64_t fileResultCount = 0;
- const auto* fileResults = getCollectionFileResults(kcb, &fileResultCount);
- if ( fileResults == nullptr ) {
- fprintf(stderr, "Could not get file results\n");
- return {};
- }
- if ( fileResultCount != 1 ) {
- fprintf(stderr, "Unsupported file result count: %lld\n", fileResultCount);
- return {};
- }
-
- CFDataRef dataRef = fileResults[0]->data;
- CFRetain(dataRef);
-
- destroyKernelCollectionBuilder(kcb);
-
- return dataRef;
-}
-
-static int createKernelCollection(const CreateKernelCollectionOptions& options) {
- // Verify any required options
- if (gOpts.archs.empty()) {
- exit_usage("-arch");
- } else {
- std::set<std::string_view> archs(gOpts.archs.begin(), gOpts.archs.end());
- if (archs.size() != gOpts.archs.size()) {
- fprintf(stderr, "Duplicate -arch specified\n");
- exit(1);
- }
- }
- if (options.outputCachePath == nullptr)
- exit_usage();
-
- switch (options.stripMode) {
- case unknownStripMode:
- case stripNone:
- break;
- case stripAll:
- case stripAllKexts:
- if ( options.collectionKind != baseKC ) {
- fprintf(stderr, "Cannot use -strip-all-kexts with auxKC. Use strip-all instead\n");
- exit(1);
- }
- break;
- }
-
- switch (options.collectionKind) {
- case unknownKC:
- fprintf(stderr, "Invalid kernel collection kind\n");
- exit(1);
- case baseKC:
- if (options.kernelPath == nullptr)
- exit_usage("-kernel");
- break;
- case pageableKC:
- case auxKC:
- if (options.kernelCollectionPath == nullptr)
- exit_usage("-kernel-collection");
- break;
- }
-
- if ( !options.bundleIDs.empty() ) {
- if (options.extensionsPath == nullptr)
- exit_usage("-extensions");
- }
-
- // Volume root should be a prefix of extensions path
- if ( options.extensionsPath != nullptr ) {
- if ( strncmp(options.extensionsPath, options.volumeRoot, strlen(options.volumeRoot)) != 0 ) {
- fprintf(stderr, "Volume root '%s' is not a prefix of extensions path '%s'\n",
- options.volumeRoot, options.extensionsPath);
- }
- }
-
- std::vector<CFDataRef> buffers;
- for (const char* arch : gOpts.archs) {
- Diagnostics diag;
- CFDataRef bufferRef = createKernelCollectionForArch(options, arch, diag);
- if ( diag.hasError() ) {
- fprintf(stderr, "%s\n", diag.errorMessage().c_str());
- return 1;
- }
- if ( bufferRef == nullptr ) {
- // If we want errors then return 0
- if ( options.printJSONErrors )
- return 0;
- return 1;
- }
- buffers.push_back(bufferRef);
- }
-
- if (buffers.size() == 1) {
- // Single arch. Just write the file directly
- CFDataRef bufferRef = buffers.front();
- if ( !safeSave(CFDataGetBytePtr(bufferRef), CFDataGetLength(bufferRef), options.outputCachePath) ) {
- fprintf(stderr, "Could not write app cache\n");
- return 1;
- }
- CFRelease(bufferRef);
- } else {
- // Multiple buffers. Create a FAT file
- std::vector<uint8_t> fatBuffer;
-
- // Add the FAT header to the start of the buffer
- fatBuffer.resize(0x4000, 0);
- fat_header* header = (fat_header*)&fatBuffer.front();
- header->magic = OSSwapHostToBigInt32(FAT_MAGIC);
- header->nfat_arch = OSSwapHostToBigInt32((uint32_t)buffers.size());
-
- for (uint32_t i = 0; i != buffers.size(); ++i) {
- CFDataRef bufferRef = buffers[i];
- mach_header* mh = (mach_header*)CFDataGetBytePtr(bufferRef);
-
- uint32_t offsetInBuffer = (uint32_t)fatBuffer.size();
-
- fat_arch* archBuffer = (fat_arch*)(&fatBuffer.front() + sizeof(fat_header));
- archBuffer[i].cputype = OSSwapHostToBigInt32(mh->cputype);
- archBuffer[i].cpusubtype = OSSwapHostToBigInt32(mh->cpusubtype);
- archBuffer[i].offset = OSSwapHostToBigInt32(offsetInBuffer);
- archBuffer[i].size = OSSwapHostToBigInt32((uint32_t)CFDataGetLength(bufferRef));
- archBuffer[i].align = OSSwapHostToBigInt32(14);
-
- auto align = [](uint64_t addr, uint8_t p2) {
- uint64_t mask = (1 << p2);
- return (addr + mask - 1) & (-mask);
- };
-
- uint32_t alignedSize = (uint32_t)align((uint32_t)CFDataGetLength(bufferRef), 14);
- fatBuffer.resize(fatBuffer.size() + alignedSize, 0);
- memcpy(&fatBuffer.front() + offsetInBuffer, CFDataGetBytePtr(bufferRef), CFDataGetLength(bufferRef));
- }
-
- if ( !safeSave(&fatBuffer.front(), fatBuffer.size(), options.outputCachePath) ) {
- fprintf(stderr, "Could not write app cache\n");
- return 1;
- }
- }
-
- return 0;
-}
-
-int main(int argc, const char* argv[]) {
- OptionsVariants options;
- if (!parseArgs(argc, argv, options))
- return 1;
-
- if (std::holds_alternative<DumpOptions>(options)) {
- return dumpAppCache(std::get<DumpOptions>(options));
- }
-
- if (std::holds_alternative<ValidateOptions>(options)) {
- return validateFile(std::get<ValidateOptions>(options));
- }
-
- if (std::holds_alternative<ListBundlesOptions>(options)) {
- return listBundles(std::get<ListBundlesOptions>(options));
- }
-
- if (std::holds_alternative<CreateKernelCollectionOptions>(options)) {
- return createKernelCollection(std::get<CreateKernelCollectionOptions>(options));
- }
-
- assert(std::holds_alternative<std::monostate>(options));
-
- exit_usage();
-}