2 * Copyright (c) 2017 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
34 #include <CoreFoundation/CFBundle.h>
35 #include <CoreFoundation/CFPropertyList.h>
36 #include <CoreFoundation/CFStream.h>
38 #include "kernel_collection_builder.h"
39 #include "ClosureFileSystemPhysical.h"
40 #include "FileUtils.h"
41 #include "JSONWriter.h"
42 #include "MachOAppCache.h"
44 using namespace dyld3::json
;
46 using dyld3::closure::FileSystemPhysical
;
47 using dyld3::closure::LoadedFileInfo
;
48 using dyld3::GradedArchs
;
49 using dyld3::MachOAnalyzer
;
50 using dyld3::MachOAppCache
;
51 using dyld3::Platform
;
52 using dyld3::json::Node
;
54 __attribute__((noreturn
))
55 static void exit_usage(const char* missingOption
= nullptr) {
56 if ( missingOption
!= nullptr ) {
57 fprintf(stderr
, "Missing option '%s'\n", missingOption
);
58 fprintf(stderr
, "\n");
61 fprintf(stderr
, "Usage: dyld_app_cache_util [-layout] [-entrypoint] [-fixups] [-symbols] [-kmod] [-uuid] [-fips] -arch arch -platform platform -app-cache app-cache-path\n");
62 fprintf(stderr
, " -layout print the layout of an existing app cache\n");
63 fprintf(stderr
, " -entrypoint print the entrypoint of an existing app cache\n");
64 fprintf(stderr
, " -fixups print the fixups of an existing app cache\n");
65 fprintf(stderr
, " -symbols print the symbols of an existing app cache\n");
66 fprintf(stderr
, " -kmod print the kmod_info of an existing app cache\n");
67 fprintf(stderr
, " -uuid print the UUID of an existing app cache\n");
68 fprintf(stderr
, " -fips print the FIPS section of an existing app cache\n");
69 fprintf(stderr
, "\n");
71 fprintf(stderr
, "Usage: dyld_app_cache_util -validate file-path -arch arch -platform platform\n");
72 fprintf(stderr
, " -validate the path to check is valid for inserting in to an app cache\n");
73 fprintf(stderr
, "\n");
75 fprintf(stderr
, "Usage: dyld_app_cache_util -list-bundles directory-path\n");
76 fprintf(stderr
, " -list-bundles the directory to index for bundles\n");
77 fprintf(stderr
, "\n");
79 fprintf(stderr
, "Usage: dyld_app_cache_util -create-kernel-collection kernel-collection -kernel kernel-path [-extensions path-to-extensions] [-bundle-id bundle-id]*\n");
80 fprintf(stderr
, " -create-kernel-collection create a kernel collection and write to the given path\n");
81 fprintf(stderr
, " -kernel path to the kernel static executable\n");
82 fprintf(stderr
, " -extensions path to the kernel extensions directory\n");
83 fprintf(stderr
, " -bundle-id zero or more bundle-ids to link in to the kernel collection\n");
84 fprintf(stderr
, " -sectcreate segment name, section name, and payload file path for more data to embed in the kernel\n");
85 fprintf(stderr
, "\n");
87 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");
88 fprintf(stderr
, " -create-pageable-kernel-collection create a pageable kernel collection and write to the given path\n");
89 fprintf(stderr
, " -kernel-collection path to the kernel collection collection\n");
90 fprintf(stderr
, " -extensions path to the kernel extensions directory\n");
91 fprintf(stderr
, " -bundle-id zero or more bundle-ids to link in to the kernel collection\n");
92 fprintf(stderr
, "\n");
94 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");
95 fprintf(stderr
, " -create-aux-kernel-collection create an aux kernel collection and write to the given path\n");
96 fprintf(stderr
, " -kernel-collection path to the kernel collection\n");
97 fprintf(stderr
, " -pageable-collection path to the pageable collection\n");
98 fprintf(stderr
, " -extensions path to the kernel extensions directory\n");
99 fprintf(stderr
, " -bundle-id zero or more bundle-ids to link in to the kernel collection\n");
100 fprintf(stderr
, "\n");
102 fprintf(stderr
, "Common options:\n");
103 fprintf(stderr
, " -arch xxx the arch to use to create the app cache\n");
104 fprintf(stderr
, " -platform xxx the platform to use to create the app cache\n");
110 bool printLayout
= false;
111 bool printEntryPoint
= false;
112 bool printFixups
= false;
113 bool printSymbols
= false;
114 bool printUUID
= false;
115 bool printKModInfo
= false;
116 bool printFIPS
= false;
119 struct ValidateOptions
{
120 const char* filePath
= nullptr;
123 struct ListBundlesOptions
{
124 const char* directoryPath
= nullptr;
127 // The payload of -sectcreate
129 const char* segmentName
= nullptr;
130 const char* sectionName
= nullptr;
131 const char* payloadFilePath
= nullptr;
134 struct CreateKernelCollectionOptions
{
135 const char* outputCachePath
= nullptr;
136 const char* kernelPath
= nullptr;
137 const char* kernelCollectionPath
= nullptr;
138 const char* pageableCollectionPath
= nullptr;
139 const char* extensionsPath
= nullptr;
140 const char* volumeRoot
= "";
141 std::vector
<const char*> bundleIDs
;
142 bool verbose
= false;
143 bool printJSONErrors
= false;
144 CollectionKind collectionKind
= unknownKC
;
145 StripMode stripMode
= unknownStripMode
;
146 std::vector
<SectionData
> sections
;
147 const char* prelinkInfoExtraData
= nullptr;
150 typedef std::variant
<std::monostate
, DumpOptions
, ValidateOptions
, ListBundlesOptions
, CreateKernelCollectionOptions
> OptionsVariants
;
152 struct CommonOptions
{
153 const char* appCachePath
= nullptr;
154 std::vector
<const char*> archs
;
155 const char* platform
= nullptr;
161 static T
& exitOrGetState(OptionsVariants
& options
, const char* argv
) {
162 if (std::holds_alternative
<std::monostate
>(options
)) {
163 return options
.emplace
<T
>();
165 if (std::holds_alternative
<T
>(options
))
166 return std::get
<T
>(options
);
170 static bool parseArgs(int argc
, const char* argv
[], OptionsVariants
& options
) {
171 for (int i
= 1; i
< argc
; ++i
) {
172 const char* arg
= argv
[i
];
174 fprintf(stderr
, "unknown option: %s\n", arg
);
179 if (strcmp(arg
, "-app-cache") == 0) {
180 if (gOpts
.appCachePath
!= nullptr)
182 gOpts
.appCachePath
= argv
[++i
];
185 if (strcmp(arg
, "-arch") == 0) {
186 gOpts
.archs
.push_back(argv
[++i
]);
189 if (strcmp(arg
, "-platform") == 0) {
190 if (gOpts
.platform
!= nullptr)
192 gOpts
.platform
= argv
[++i
];
197 if (strcmp(arg
, "-layout") == 0) {
198 exitOrGetState
<DumpOptions
>(options
, arg
).printLayout
= true;
201 if (strcmp(arg
, "-entrypoint") == 0) {
202 exitOrGetState
<DumpOptions
>(options
, arg
).printEntryPoint
= true;
205 if (strcmp(arg
, "-fixups") == 0) {
206 exitOrGetState
<DumpOptions
>(options
, arg
).printFixups
= true;
209 if (strcmp(arg
, "-symbols") == 0) {
210 exitOrGetState
<DumpOptions
>(options
, arg
).printSymbols
= true;
213 if (strcmp(arg
, "-uuid") == 0) {
214 exitOrGetState
<DumpOptions
>(options
, arg
).printUUID
= true;
217 if (strcmp(arg
, "-kmod") == 0) {
218 exitOrGetState
<DumpOptions
>(options
, arg
).printKModInfo
= true;
221 if (strcmp(arg
, "-fips") == 0) {
222 exitOrGetState
<DumpOptions
>(options
, arg
).printFIPS
= true;
227 if (strcmp(arg
, "-validate") == 0) {
228 exitOrGetState
<ValidateOptions
>(options
, arg
).filePath
= argv
[++i
];
232 // ListBundlesOptions
233 if (strcmp(arg
, "-list-bundles") == 0) {
234 exitOrGetState
<ListBundlesOptions
>(options
, arg
).directoryPath
= argv
[++i
];
238 // CreateKernelCollectionOptions
239 if (strcmp(arg
, "-create-kernel-collection") == 0) {
240 exitOrGetState
<CreateKernelCollectionOptions
>(options
, arg
).outputCachePath
= argv
[++i
];
241 exitOrGetState
<CreateKernelCollectionOptions
>(options
, arg
).collectionKind
= baseKC
;
244 if (strcmp(arg
, "-kernel") == 0) {
245 exitOrGetState
<CreateKernelCollectionOptions
>(options
, arg
).kernelPath
= argv
[++i
];
248 if (strcmp(arg
, "-create-pageable-kernel-collection") == 0) {
249 exitOrGetState
<CreateKernelCollectionOptions
>(options
, arg
).outputCachePath
= argv
[++i
];
250 exitOrGetState
<CreateKernelCollectionOptions
>(options
, arg
).collectionKind
= pageableKC
;
253 if (strcmp(arg
, "-create-aux-kernel-collection") == 0) {
254 exitOrGetState
<CreateKernelCollectionOptions
>(options
, arg
).outputCachePath
= argv
[++i
];
255 exitOrGetState
<CreateKernelCollectionOptions
>(options
, arg
).collectionKind
= auxKC
;
258 if (strcmp(arg
, "-kernel-collection") == 0) {
259 exitOrGetState
<CreateKernelCollectionOptions
>(options
, arg
).kernelCollectionPath
= argv
[++i
];
262 if (strcmp(arg
, "-pageable-collection") == 0) {
263 exitOrGetState
<CreateKernelCollectionOptions
>(options
, arg
).pageableCollectionPath
= argv
[++i
];
266 if (strcmp(arg
, "-extensions") == 0) {
267 exitOrGetState
<CreateKernelCollectionOptions
>(options
, arg
).extensionsPath
= argv
[++i
];
270 if (strcmp(arg
, "-volume-root") == 0) {
271 exitOrGetState
<CreateKernelCollectionOptions
>(options
, arg
).volumeRoot
= argv
[++i
];
274 if (strcmp(arg
, "-bundle-id") == 0) {
275 exitOrGetState
<CreateKernelCollectionOptions
>(options
, arg
).bundleIDs
.push_back(argv
[++i
]);
278 if (strcmp(arg
, "-verbose") == 0) {
279 exitOrGetState
<CreateKernelCollectionOptions
>(options
, arg
).verbose
= true;
282 if (strcmp(arg
, "-json-errors") == 0) {
283 exitOrGetState
<CreateKernelCollectionOptions
>(options
, arg
).printJSONErrors
= true;
286 if (strcmp(arg
, "-strip-all") == 0) {
287 exitOrGetState
<CreateKernelCollectionOptions
>(options
, arg
).stripMode
= stripAll
;
290 if (strcmp(arg
, "-strip-all-kexts") == 0) {
291 exitOrGetState
<CreateKernelCollectionOptions
>(options
, arg
).stripMode
= stripAllKexts
;
294 if (strcmp(arg
, "-sectcreate") == 0) {
295 const char* segmentName
= argv
[++i
];
296 const char* sectionName
= argv
[++i
];
297 const char* payloadFilePath
= argv
[++i
];
298 SectionData sectData
= { segmentName
, sectionName
, payloadFilePath
};
299 exitOrGetState
<CreateKernelCollectionOptions
>(options
, arg
).sections
.push_back(sectData
);
302 if (strcmp(arg
, "-prelink-info-extra") == 0) {
303 const char* payloadFilePath
= argv
[++i
];
304 exitOrGetState
<CreateKernelCollectionOptions
>(options
, arg
).prelinkInfoExtraData
= payloadFilePath
;
309 fprintf(stderr
, "unknown option: %s\n", arg
);
316 static Platform
stringToPlatform(const std::string
& str
) {
317 if (str
== "unknown")
318 return Platform::unknown
;
320 return Platform::macOS
;
322 return Platform::iOS
;
324 return Platform::tvOS
;
325 if (str
== "watchOS")
326 return Platform::watchOS
;
327 if (str
== "bridgeOS")
328 return Platform::bridgeOS
;
330 return Platform::iOSMac
;
331 if (str
== "UIKitForMac")
332 return Platform::iOSMac
;
333 if (str
== "iOS_simulator")
334 return Platform::iOS_simulator
;
335 if (str
== "tvOS_simulator")
336 return Platform::tvOS_simulator
;
337 if (str
== "watchOS_simulator")
338 return Platform::watchOS_simulator
;
339 return Platform::unknown
;
342 static int dumpAppCache(const DumpOptions
& options
) {
343 // Verify any required options
344 if (gOpts
.archs
.size() != 1)
346 if (gOpts
.platform
== nullptr)
347 exit_usage("-platform");
349 if (gOpts
.appCachePath
== nullptr)
352 FileSystemPhysical fileSystem
;
353 if (!fileSystem
.fileExists(gOpts
.appCachePath
)) {
354 fprintf(stderr
, "App-cache path does not exist: %s\n", gOpts
.appCachePath
);
358 const GradedArchs
& archs
= GradedArchs::forName(gOpts
.archs
[0]);
359 Platform platform
= Platform::unknown
;
360 bool isKernelCollection
= false;
362 // HACK: Pass a real option for building a kernel app cache
363 if (!strcmp(gOpts
.platform
, "kernel")) {
364 isKernelCollection
= true;
366 platform
= stringToPlatform(gOpts
.platform
);
367 if (platform
== Platform::unknown
) {
368 fprintf(stderr
, "Could not create app cache because: unknown platform '%s'\n", gOpts
.platform
);
373 __block Diagnostics diag
;
374 char appCacheRealPath
[MAXPATHLEN
];
375 LoadedFileInfo loadedFileInfo
= MachOAnalyzer::load(diag
, fileSystem
, gOpts
.appCachePath
, archs
, platform
, appCacheRealPath
);
376 if (diag
.hasError()) {
377 fprintf(stderr
, "Could not load app cache because: %s\n", diag
.errorMessage().c_str());
381 MachOAppCache
* appCacheMA
= (MachOAppCache
*)loadedFileInfo
.fileContent
;
382 if (appCacheMA
== nullptr) {
383 fprintf(stderr
, "Could not load app cache: %s\n", gOpts
.appCachePath
);
387 if (options
.printLayout
) {
388 __block Node topNode
;
390 // Add the segments for the app cache
391 __block Node segmentsNode
;
392 __block
bool hasError
= false;
393 appCacheMA
->forEachSegment(^(const dyld3::MachOFile::SegmentInfo
&info
, bool &stop
) {
395 segmentNode
.map
["name"] = makeNode(info
.segName
);
396 segmentNode
.map
["vmAddr"] = makeNode(hex(info
.vmAddr
));
397 segmentNode
.map
["vmSize"] = makeNode(hex(info
.vmSize
));
398 segmentNode
.map
["vmEnd"] = makeNode(hex(info
.vmAddr
+ info
.vmSize
));
399 switch (info
.protections
) {
401 segmentNode
.map
["permissions"] = makeNode("r--");
404 segmentNode
.map
["permissions"] = makeNode("-w-");
406 case VM_PROT_EXECUTE
:
407 segmentNode
.map
["permissions"] = makeNode("--x");
409 case VM_PROT_READ
| VM_PROT_WRITE
:
410 segmentNode
.map
["permissions"] = makeNode("rw-");
412 case VM_PROT_READ
| VM_PROT_EXECUTE
:
413 segmentNode
.map
["permissions"] = makeNode("r-x");
415 case VM_PROT_READ
| VM_PROT_WRITE
| VM_PROT_EXECUTE
:
416 segmentNode
.map
["permissions"] = makeNode("rwx");
419 fprintf(stderr
, "Unknown permissions on segment '%s'\n", info
.segName
);
424 __block Node sectionsNode
;
425 appCacheMA
->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo
§Info
, bool malformedSectionRange
, bool &stop
) {
426 if ( strncmp(sectInfo
.segInfo
.segName
, info
.segName
, 16) != 0 )
430 sectionNode
.map
["name"] = makeNode(sectInfo
.sectName
);
431 sectionNode
.map
["vmAddr"] = makeNode(hex(sectInfo
.sectAddr
));
432 sectionNode
.map
["vmSize"] = makeNode(hex(sectInfo
.sectSize
));
433 sectionNode
.map
["vmEnd"] = makeNode(hex(sectInfo
.sectAddr
+ sectInfo
.sectSize
));
435 sectionsNode
.array
.push_back(sectionNode
);
438 if ( !sectionsNode
.array
.empty() ) {
439 segmentNode
.map
["sections"] = sectionsNode
;
442 segmentsNode
.array
.push_back(segmentNode
);
448 topNode
.map
["cache-segments"] = segmentsNode
;
450 // Map from name to relative path
451 __block
std::unordered_map
<std::string
, std::string
> relativePaths
;
452 appCacheMA
->forEachPrelinkInfoLibrary(diag
, ^(const char *bundleName
, const char* relativePath
,
453 const std::vector
<const char *> &deps
) {
454 if ( relativePath
!= nullptr )
455 relativePaths
[bundleName
] = relativePath
;
458 __block Node dylibsNode
;
459 appCacheMA
->forEachDylib(diag
, ^(const MachOAnalyzer
*ma
, const char *name
, bool &stop
) {
460 __block Node segmentsNode
;
461 ma
->forEachSegment(^(const dyld3::MachOFile::SegmentInfo
&info
, bool &stop
) {
463 segmentNode
.map
["name"] = makeNode(info
.segName
);
464 segmentNode
.map
["vmAddr"] = makeNode(hex(info
.vmAddr
));
465 segmentNode
.map
["vmSize"] = makeNode(hex(info
.vmSize
));
466 segmentNode
.map
["vmEnd"] = makeNode(hex(info
.vmAddr
+ info
.vmSize
));
468 switch (info
.protections
) {
470 segmentNode
.map
["permissions"] = makeNode("r--");
473 segmentNode
.map
["permissions"] = makeNode("-w-");
475 case VM_PROT_EXECUTE
:
476 segmentNode
.map
["permissions"] = makeNode("--x");
478 case VM_PROT_READ
| VM_PROT_WRITE
:
479 segmentNode
.map
["permissions"] = makeNode("rw-");
481 case VM_PROT_READ
| VM_PROT_EXECUTE
:
482 segmentNode
.map
["permissions"] = makeNode("r-x");
485 fprintf(stderr
, "Unknown permissions on segment '%s'\n", info
.segName
);
490 __block Node sectionsNode
;
491 ma
->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo
§Info
, bool malformedSectionRange
, bool &stop
) {
492 if ( strncmp(sectInfo
.segInfo
.segName
, info
.segName
, 16) != 0 )
496 sectionNode
.map
["name"] = makeNode(sectInfo
.sectName
);
497 sectionNode
.map
["vmAddr"] = makeNode(hex(sectInfo
.sectAddr
));
498 sectionNode
.map
["vmSize"] = makeNode(hex(sectInfo
.sectSize
));
499 sectionNode
.map
["vmEnd"] = makeNode(hex(sectInfo
.sectAddr
+ sectInfo
.sectSize
));
501 sectionsNode
.array
.push_back(sectionNode
);
504 if ( !sectionsNode
.array
.empty() ) {
505 segmentNode
.map
["sections"] = sectionsNode
;
508 segmentsNode
.array
.push_back(segmentNode
);
512 dylibNode
.map
["name"] = makeNode(name
);
513 dylibNode
.map
["segments"] = segmentsNode
;
515 auto relativePathIt
= relativePaths
.find(name
);
516 if ( relativePathIt
!= relativePaths
.end() )
517 dylibNode
.map
["relativePath"] = makeNode(relativePathIt
->second
);
519 dylibsNode
.array
.push_back(dylibNode
);
522 topNode
.map
["dylibs"] = dylibsNode
;
524 printJSON(topNode
, 0, std::cout
);
527 if (options
.printEntryPoint
) {
528 __block Node topNode
;
531 uint64_t entryOffset
;
534 if ( appCacheMA
->getEntry(entryOffset
, usesCRT
) ) {
535 entryPointNode
.value
= hex(appCacheMA
->preferredLoadAddress() + entryOffset
);
538 topNode
.map
["entrypoint"] = entryPointNode
;
540 printJSON(topNode
, 0, std::cout
);
543 if (options
.printFixups
) {
544 __block Node topNode
;
546 __block
uint64_t baseAddress
= ~0ULL;
547 appCacheMA
->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo
& info
, bool& stop
) {
548 baseAddress
= std::min(baseAddress
, info
.vmAddr
);
550 uint64_t cacheBaseAddress
= baseAddress
;
551 uint64_t textSegVMAddr
= appCacheMA
->preferredLoadAddress();
553 auto getFixupsNode
= [cacheBaseAddress
, textSegVMAddr
](const dyld3::MachOAnalyzer
* ma
) {
554 __block Node fixupsNode
;
556 if (!ma
->hasChainedFixups()) {
557 return makeNode("none");
560 // Keep track of the fixups seen by chained fixups. The remainder might be
561 // classic relocs if we are the x86_64 kernel collection
562 __block
std::set
<uint64_t> seenFixupVMOffsets
;
564 __block Diagnostics diag
;
565 ma
->withChainStarts(diag
, 0, ^(const dyld_chained_starts_in_image
* starts
) {
566 ma
->forEachFixupInAllChains(diag
, starts
, false, ^(dyld3::MachOLoaded::ChainedFixupPointerOnDisk
* fixupLoc
, const dyld_chained_starts_in_segment
* segInfo
, bool& stop
) {
567 uint64_t vmOffset
= (uint8_t*)fixupLoc
- (uint8_t*)ma
;
568 seenFixupVMOffsets
.insert(vmOffset
);
570 // Correct for __DATA being before __TEXT, in which case the offset
571 // is from __DATA, not a mach header offset
572 vmOffset
+= (textSegVMAddr
- cacheBaseAddress
);
574 fixupsNode
.map
[hex(vmOffset
)] = makeNode("fixup");
575 switch (segInfo
->pointer_format
) {
576 case DYLD_CHAINED_PTR_64_KERNEL_CACHE
:
577 case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE
: {
578 uint64_t targetVMOffset
= fixupLoc
->kernel64
.target
;
579 uint64_t targetVMAddr
= targetVMOffset
+ cacheBaseAddress
;
580 std::string level
= "kc(" + decimal(fixupLoc
->kernel64
.cacheLevel
) + ")";
581 std::string fixup
= level
+ " + " + hex(targetVMAddr
);
582 if (fixupLoc
->kernel64
.isAuth
) {
584 fixup
+= fixupLoc
->kernel64
.keyName();
586 fixup
+= fixupLoc
->kernel64
.addrDiv
? "addr" : "!addr";
588 fixup
+= decimal(fixupLoc
->kernel64
.diversity
);
591 fixupsNode
.map
[hex(vmOffset
)] = makeNode(fixup
);
595 diag
.error("unknown pointer type %d", segInfo
->pointer_format
);
600 diag
.assertNoError();
602 ma
->forEachRebase(diag
, ^(const char *opcodeName
, const dyld3::MachOAnalyzer::LinkEditInfo
&leInfo
,
603 const dyld3::MachOAnalyzer::SegmentInfo
*segments
,
604 bool segIndexSet
, uint32_t pointerSize
, uint8_t segmentIndex
,
605 uint64_t segmentOffset
, dyld3::MachOAnalyzer::Rebase kind
, bool &stop
) {
606 uint64_t rebaseVmAddr
= segments
[segmentIndex
].vmAddr
+ segmentOffset
;
607 uint64_t runtimeOffset
= rebaseVmAddr
- textSegVMAddr
;
608 const uint8_t* fixupLoc
= (const uint8_t*)ma
+ runtimeOffset
;
610 // Correct for __DATA being before __TEXT, in which case the offset
611 // is from __DATA, not a mach header offset
612 runtimeOffset
+= (textSegVMAddr
- cacheBaseAddress
);
614 std::string fixup
= "kc(0) + ";
616 case dyld3::MachOAnalyzer::Rebase::unknown
:
617 fixup
+= " : unhandled";
619 case dyld3::MachOAnalyzer::Rebase::pointer32
: {
620 uint32_t value
= *(uint32_t*)(fixupLoc
);
621 fixup
+= hex(value
) + " : pointer32";
624 case dyld3::MachOAnalyzer::Rebase::pointer64
: {
625 uint64_t value
= *(uint64_t*)(fixupLoc
);
626 fixup
+= hex(value
) + " : pointer64";
629 case dyld3::MachOAnalyzer::Rebase::textPCrel32
:
630 fixup
+= " : pcrel32";
632 case dyld3::MachOAnalyzer::Rebase::textAbsolute32
:
633 fixup
+= " : absolute32";
636 fixupsNode
.map
[hex(runtimeOffset
)] = makeNode(fixup
);
638 diag
.assertNoError();
643 topNode
.map
["fixups"] = getFixupsNode(appCacheMA
);
645 __block Node dylibsNode
;
646 appCacheMA
->forEachDylib(diag
, ^(const MachOAnalyzer
*ma
, const char *name
, bool &stop
) {
648 dylibNode
.map
["name"] = makeNode(name
);
649 dylibNode
.map
["fixups"] = getFixupsNode(ma
);
651 dylibsNode
.array
.push_back(dylibNode
);
654 topNode
.map
["dylibs"] = dylibsNode
;
656 printJSON(topNode
, 0, std::cout
);
659 if (options
.printSymbols
) {
660 __block Node topNode
;
662 __block Node dylibsNode
;
663 appCacheMA
->forEachDylib(diag
, ^(const MachOAnalyzer
*ma
, const char *name
, bool &stop
) {
664 __block Node globalSymbolsNode
;
665 ma
->forEachGlobalSymbol(diag
, ^(const char *symbolName
, uint64_t n_value
, uint8_t n_type
, uint8_t n_sect
, uint16_t n_desc
, bool &stop
) {
667 symbolNode
.map
["name"] = makeNode(symbolName
);
668 symbolNode
.map
["vmAddr"] = makeNode(hex(n_value
));
670 globalSymbolsNode
.array
.push_back(symbolNode
);
673 __block Node localSymbolsNode
;
674 ma
->forEachLocalSymbol(diag
, ^(const char *symbolName
, uint64_t n_value
, uint8_t n_type
, uint8_t n_sect
, uint16_t n_desc
, bool &stop
) {
676 symbolNode
.map
["name"] = makeNode(symbolName
);
677 symbolNode
.map
["vmAddr"] = makeNode(hex(n_value
));
679 localSymbolsNode
.array
.push_back(symbolNode
);
682 if (globalSymbolsNode
.array
.empty())
683 globalSymbolsNode
= makeNode("none");
684 if (localSymbolsNode
.array
.empty())
685 localSymbolsNode
= makeNode("none");
688 dylibNode
.map
["name"] = makeNode(name
);
689 dylibNode
.map
["global-symbols"] = globalSymbolsNode
;
690 dylibNode
.map
["local-symbols"] = localSymbolsNode
;
692 dylibsNode
.array
.push_back(dylibNode
);
695 topNode
.map
["dylibs"] = dylibsNode
;
697 printJSON(topNode
, 0, std::cout
);
700 if (options
.printUUID
) {
701 __block Node topNode
;
706 if ( appCacheMA
->getUuid(uuid
) ) {
707 uuid_string_t uuidString
;
708 uuid_unparse_upper(uuid
, uuidString
);
709 uuidNode
.value
= uuidString
;
712 topNode
.map
["uuid"] = uuidNode
;
714 auto getPlistUUID
= ^(const char* jsonNodeName
, CFStringRef keyName
) {
716 const uint8_t* prelinkInfoBuffer
= nullptr;
717 uint64_t prelinkInfoBufferSize
= 0;
718 prelinkInfoBuffer
= (const uint8_t*)appCacheMA
->findSectionContent("__PRELINK_INFO", "__info", prelinkInfoBufferSize
);
719 if ( prelinkInfoBuffer
!= nullptr ) {
720 CFReadStreamRef readStreamRef
= CFReadStreamCreateWithBytesNoCopy(kCFAllocatorDefault
, prelinkInfoBuffer
, prelinkInfoBufferSize
, kCFAllocatorNull
);
721 if ( !CFReadStreamOpen(readStreamRef
) ) {
722 fprintf(stderr
, "Could not open plist stream\n");
725 CFErrorRef errorRef
= nullptr;
726 CFPropertyListRef plistRef
= CFPropertyListCreateWithStream(kCFAllocatorDefault
, readStreamRef
, prelinkInfoBufferSize
, kCFPropertyListImmutable
, nullptr, &errorRef
);
727 if ( errorRef
!= nullptr ) {
728 CFStringRef stringRef
= CFErrorCopyFailureReason(errorRef
);
729 fprintf(stderr
, "Could not read plist because: %s\n", CFStringGetCStringPtr(stringRef
, kCFStringEncodingASCII
));
730 CFRelease(stringRef
);
733 assert(CFGetTypeID(plistRef
) == CFDictionaryGetTypeID());
734 CFDataRef uuidDataRef
= (CFDataRef
)CFDictionaryGetValue((CFDictionaryRef
)plistRef
, keyName
);
735 if ( uuidDataRef
!= nullptr ) {
736 CFDataGetBytes(uuidDataRef
, CFRangeMake(0, CFDataGetLength(uuidDataRef
)), uuid
);
739 uuid_string_t uuidString
;
740 uuid_unparse_upper(uuid
, uuidString
);
741 uuidNode
.value
= uuidString
;
742 topNode
.map
[jsonNodeName
] = uuidNode
;
745 CFRelease(readStreamRef
);
749 getPlistUUID("prelink-info-uuid", CFSTR("_PrelinkKCID"));
751 // If we are an auxKC, then we should also have a reference to the baseKC UUID
752 getPlistUUID("prelink-info-base-uuid", CFSTR("_BootKCID"));
754 // If we are an pageableKC, then we should also have a reference to the pageableKC UUID
755 getPlistUUID("prelink-info-pageable-uuid", CFSTR("_PageableKCID"));
757 printJSON(topNode
, 0, std::cout
);
760 if (options
.printKModInfo
) {
761 __block Node topNode
;
763 appCacheMA
->forEachDylib(diag
, ^(const MachOAnalyzer
*ma
, const char *name
, bool &stop
) {
765 dylibNode
.map
["name"] = makeNode(name
);
767 // Check for a global first
768 __block
uint64_t kmodInfoVMOffset
= 0;
769 __block
bool found
= false;
771 dyld3::MachOAnalyzer::FoundSymbol foundInfo
;
772 found
= ma
->findExportedSymbol(diag
, "_kmod_info", true, foundInfo
, nullptr);
774 kmodInfoVMOffset
= foundInfo
.value
;
777 // And fall back to a local if we need to
779 ma
->forEachLocalSymbol(diag
, ^(const char* aSymbolName
, uint64_t n_value
, uint8_t n_type
,
780 uint8_t n_sect
, uint16_t n_desc
, bool& stop
) {
781 if ( strcmp(aSymbolName
, "_kmod_info") == 0 ) {
782 kmodInfoVMOffset
= n_value
- ma
->preferredLoadAddress();
790 dyld3::MachOAppCache::KModInfo64_v1
* kmodInfo
= (dyld3::MachOAppCache::KModInfo64_v1
*)((uint8_t*)ma
+ kmodInfoVMOffset
);
792 kmodInfoNode
.map
["info-version"] = makeNode(decimal(kmodInfo
->info_version
));
793 kmodInfoNode
.map
["name"] = makeNode((const char*)&kmodInfo
->name
[0]);
794 kmodInfoNode
.map
["version"] = makeNode((const char*)&kmodInfo
->version
[0]);
795 kmodInfoNode
.map
["address"] = makeNode(hex(kmodInfo
->address
));
796 kmodInfoNode
.map
["size"] = makeNode(hex(kmodInfo
->size
));
798 dylibNode
.map
["kmod_info"] = kmodInfoNode
;
800 dylibNode
.map
["kmod_info"] = makeNode("none");
803 topNode
.array
.push_back(dylibNode
);
806 printJSON(topNode
, 0, std::cout
);
809 if (options
.printFIPS
) {
810 __block Node topNode
;
812 appCacheMA
->forEachDylib(diag
, ^(const MachOAnalyzer
*ma
, const char *name
, bool &stop
) {
813 if ( strcmp(name
, "com.apple.kec.corecrypto") != 0 )
816 uint64_t hashStoreSize
;
817 const void* hashStoreLocation
= ma
->findSectionContent("__TEXT", "__fips_hmacs", hashStoreSize
);
818 assert(hashStoreLocation
!= nullptr);
820 const uint8_t* hashBuffer
= (const uint8_t*)hashStoreLocation
;
821 std::string hashString
;
823 for (int i
= 0; i
< hashStoreSize
; ++i
) {
824 uint8_t byte
= hashBuffer
[i
];
825 uint8_t nibbleL
= byte
& 0x0F;
826 uint8_t nibbleH
= byte
>> 4;
827 if ( nibbleH
< 10 ) {
828 hashString
+= '0' + nibbleH
;
830 hashString
+= 'a' + (nibbleH
-10);
832 if ( nibbleL
< 10 ) {
833 hashString
+= '0' + nibbleL
;
835 hashString
+= 'a' + (nibbleL
-10);
841 topNode
.map
["fips"] = makeNode(hashString
);
844 printJSON(topNode
, 0, std::cout
);
850 static int validateFile(const ValidateOptions
& options
) {
851 // Verify any required options
852 if (gOpts
.archs
.size() != 1)
854 if (gOpts
.platform
== nullptr)
855 exit_usage("-platform");
856 if (options
.filePath
== nullptr)
859 const GradedArchs
& archs
= GradedArchs::forName(gOpts
.archs
[0]);
860 Platform platform
= Platform::unknown
;
862 // HACK: Pass a real option for building a kernel app cache
863 if (strcmp(gOpts
.platform
, "kernel")) {
864 fprintf(stderr
, "Could not create app cache because: unknown platform '%s'\n", gOpts
.platform
);
868 __block Diagnostics diag
;
870 std::string file
= options
.filePath
;
872 FileSystemPhysical fileSystem
;
873 char fileRealPath
[MAXPATHLEN
];
874 LoadedFileInfo loadedFileInfo
= MachOAnalyzer::load(diag
, fileSystem
, file
.c_str(), archs
, platform
, fileRealPath
);
875 if (diag
.hasError()) {
876 fprintf(stderr
, "Could not load file '%s' because: %s\n", file
.c_str(), diag
.errorMessage().c_str());
881 MachOAnalyzer
* ma
= (MachOAnalyzer
*)loadedFileInfo
.fileContent
;
883 fprintf(stderr
, "Could not load file: %s\n", file
.c_str());
887 auto errorHandler
= ^(const char* msg
) {
888 diag
.warning("File '%s' cannot be placed in kernel collection because: %s", file
.c_str(), msg
);
890 if (ma
->canBePlacedInKernelCollection(file
.c_str(), errorHandler
)) {
893 fileSystem
.unloadFile(loadedFileInfo
);
898 // Since we found no files, print warnings for the ones we tried
899 if (diag
.warnings().empty()) {
900 fprintf(stderr
, "File '%s' was not valid for app-cache\n", file
.c_str());
902 for (const std::string
& msg
: diag
.warnings()) {
903 fprintf(stderr
, " %s\n", msg
.c_str());
912 static void forEachBundle(const char* bundlesDirectoryPath
,
913 void (^callback
)(CFBundleRef bundleRef
, const char* bundleName
)) {
914 CFStringRef sourcePath
= CFStringCreateWithCStringNoCopy(kCFAllocatorDefault
, bundlesDirectoryPath
,
915 kCFStringEncodingASCII
, kCFAllocatorNull
);
916 CFURLRef sourceURL
= CFURLCreateWithFileSystemPath(kCFAllocatorDefault
, sourcePath
,
917 kCFURLPOSIXPathStyle
, true);
918 CFArrayRef bundles
= CFBundleCreateBundlesFromDirectory(kCFAllocatorDefault
, sourceURL
, nullptr);
920 for (CFIndex i
= 0, e
= CFArrayGetCount(bundles
); i
!= e
; ++i
) {
921 CFBundleRef bundleRef
= (CFBundleRef
)CFArrayGetValueAtIndex(bundles
, i
);
922 CFStringRef bundleID
= CFBundleGetIdentifier(bundleRef
);
925 const char* bundleName
= CFStringGetCStringPtr(bundleID
, kCFStringEncodingASCII
);
926 callback(bundleRef
, bundleName
);
929 CFRelease(sourcePath
);
930 CFRelease(sourceURL
);
934 static int listBundles(const ListBundlesOptions
& options
) {
935 // Verify any required options
936 if (options
.directoryPath
== nullptr)
939 forEachBundle(options
.directoryPath
, ^(CFBundleRef bundleRef
, const char* bundleName
){
940 printf("Bundle: %s\n", bundleName
);
947 createKernelCollectionForArch(const CreateKernelCollectionOptions
& options
, const char* arch
,
949 const GradedArchs
& archs
= GradedArchs::forName(arch
);
950 Platform platform
= Platform::unknown
;
953 KernelCollectionBuilder
* kcb
= nullptr;
955 CFStringRef archStringRef
= CFStringCreateWithCString(kCFAllocatorDefault
, arch
, kCFStringEncodingASCII
);
956 BuildOptions_v1 buildOptions
= { 1, options
.collectionKind
, options
.stripMode
, archStringRef
, options
.verbose
};
957 kcb
= createKernelCollectionBuilder(&buildOptions
);
958 CFRelease(archStringRef
);
961 FileSystemPhysical fileSystem
;
962 LoadedFileInfo kernelCollectionLoadedFileInfo
;
964 auto loadKernelCollection
= ^(const char* kernelCollectionPath
, CollectionKind collectionKind
) {
965 if (!fileSystem
.fileExists(kernelCollectionPath
)) {
966 fprintf(stderr
, "kernel collection path does not exist: %s\n", options
.kernelPath
);
970 char realerPath
[MAXPATHLEN
];
971 bool loadedFile
= fileSystem
.loadFile(kernelCollectionPath
, info
, realerPath
, ^(const char *format
, ...) {
973 va_start(list
, format
);
974 diag
.error(format
, list
);
979 CFStringRef pathStringRef
= CFStringCreateWithCString(kCFAllocatorDefault
, kernelCollectionPath
, kCFStringEncodingASCII
);
980 CFDataRef dataRef
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, (const uint8_t*)info
.fileContent
, info
.fileContentLen
, kCFAllocatorNull
);
981 if ( !addCollectionFile(kcb
, pathStringRef
, dataRef
, collectionKind
) ) {
982 diag
.error("Could not load kernel collection file");
986 CFRelease(pathStringRef
);
990 switch (options
.collectionKind
) {
992 fprintf(stderr
, "Invalid kernel collection kind\n");
995 if (!fileSystem
.fileExists(options
.kernelPath
)) {
996 fprintf(stderr
, "Kernel path does not exist: %s\n", options
.kernelPath
);
1000 char realerPath
[MAXPATHLEN
];
1001 bool loadedFile
= fileSystem
.loadFile(options
.kernelPath
, info
, realerPath
, ^(const char *format
, ...) {
1003 va_start(list
, format
);
1004 diag
.error(format
, list
);
1009 CFStringRef pathStringRef
= CFStringCreateWithCString(kCFAllocatorDefault
, options
.kernelPath
, kCFStringEncodingASCII
);
1010 CFDataRef dataRef
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, (const uint8_t*)info
.fileContent
, info
.fileContentLen
, kCFAllocatorNull
);
1011 if ( !addKernelFile(kcb
, pathStringRef
, dataRef
) ) {
1012 uint64_t errorCount
= 0;
1013 const char* const* errors
= getErrors(kcb
, &errorCount
);
1014 for (uint64_t i
= 0; i
!= errorCount
; ++i
)
1015 diag
.error("Could not load kernel file because: '%s'", errors
[i
]);
1019 CFRelease(pathStringRef
);
1023 if ( !loadKernelCollection(options
.kernelCollectionPath
, baseKC
) )
1027 if ( !loadKernelCollection(options
.kernelCollectionPath
, baseKC
) )
1029 // Pageable is optional
1030 if ( options
.pageableCollectionPath
!= nullptr ) {
1031 if ( !loadKernelCollection(options
.pageableCollectionPath
, pageableKC
) )
1037 if ( !options
.bundleIDs
.empty() ) {
1039 std::string executablePath
;
1040 std::string bundlePath
;
1041 std::vector
<std::string
> dependencies
;
1042 CFDictionaryRef infoPlist
= nullptr;
1044 __block
std::map
<std::string
, BundleData
> foundBundles
;
1046 // Look for bundles in the extensions directory and any PlugIns directories its kext's contain
1047 __block
std::list
<std::pair
<std::string
, bool>> kextDirectoriesToProcess
;
1048 kextDirectoriesToProcess
.push_back({ options
.extensionsPath
, true });
1049 while ( !kextDirectoriesToProcess
.empty() ) {
1050 std::string kextDir
= kextDirectoriesToProcess
.front().first
;
1051 bool lookForPlugins
= kextDirectoriesToProcess
.front().second
;
1052 kextDirectoriesToProcess
.pop_front();
1054 __block
bool foundError
= false;
1055 forEachBundle(kextDir
.c_str(), ^(CFBundleRef bundleRef
, const char* bundleName
) {
1060 // If the directory contains a PlugIns directory, then add it to the list to seach for kexts
1061 if (lookForPlugins
) {
1062 CFURLRef pluginsRelativeURL
= CFBundleCopyBuiltInPlugInsURL(bundleRef
);
1063 if ( pluginsRelativeURL
!= nullptr ) {
1064 CFURLRef pluginsAbsoluteURL
= CFURLCopyAbsoluteURL(pluginsRelativeURL
);
1065 CFStringRef pluginString
= CFURLCopyFileSystemPath(pluginsAbsoluteURL
, kCFURLPOSIXPathStyle
);
1066 const char* pluginPath
= CFStringGetCStringPtr(pluginString
, kCFStringEncodingASCII
);
1067 kextDirectoriesToProcess
.push_back({ pluginPath
, false });
1069 CFRelease(pluginString
);
1070 CFRelease(pluginsAbsoluteURL
);
1071 CFRelease(pluginsRelativeURL
);
1076 // For now always load bundles as we don't require every bundle to be listed on the command line
1077 // but can instead bring them in on demand.
1079 // Once we've looked for plugins, if we don't want this bundle then we can skip validating it.
1080 if ( foundBundles
.count(bundleName
) == 0 )
1084 BundleData bundleData
;
1085 bundleData
.infoPlist
= CFBundleGetInfoDictionary(bundleRef
);
1087 CFURLRef bundleExecutableRelativeURL
= CFBundleCopyExecutableURL(bundleRef
);
1089 // Its ok to be missing an executable. We'll just skip this bundle
1090 if ( bundleExecutableRelativeURL
== nullptr ) {
1091 // FIXME: Its possibly not ok to be missing the executable if its actually listed
1092 // as a CFBundleExecutable path in the plist
1093 foundBundles
[bundleName
] = bundleData
;
1097 CFURLRef bundleExecutableAbsoluteURL
= CFURLCopyAbsoluteURL(bundleExecutableRelativeURL
);
1098 CFStringRef bundleExecutableString
= CFURLCopyFileSystemPath(bundleExecutableAbsoluteURL
, kCFURLPOSIXPathStyle
);
1099 const char* bundleExecutablePath
= CFStringGetCStringPtr(bundleExecutableString
, kCFStringEncodingASCII
);
1101 // Check for an arch specific dependency list first
1102 std::string archBundleLibraries
= std::string("OSBundleLibraries") + "_" + arch
;
1103 CFStringRef archBundleLibrariesStringRef
= CFStringCreateWithCStringNoCopy(kCFAllocatorDefault
, archBundleLibraries
.c_str(),
1104 kCFStringEncodingASCII
, kCFAllocatorNull
);
1105 CFTypeRef depsRef
= CFBundleGetValueForInfoDictionaryKey(bundleRef
, archBundleLibrariesStringRef
);
1106 if ( depsRef
== nullptr ) {
1107 // No arch specific deps, so try the defaults
1108 depsRef
= CFBundleGetValueForInfoDictionaryKey(bundleRef
, CFSTR("OSBundleLibraries"));
1110 if (depsRef
!= nullptr) {
1111 if (CFGetTypeID(depsRef
) != CFDictionaryGetTypeID()) {
1112 fprintf(stderr
, "Bad bundle '%s' (\"OSBundleLibraries\" is not a dictionary)\n", bundleName
);
1117 CFDictionaryRef dictRef
= (CFDictionaryRef
)depsRef
;
1118 CFDictionaryApplyFunction(dictRef
, [](const void *key
, const void *value
, void *context
) {
1119 BundleData
* bundleData
= (BundleData
*)context
;
1120 CFStringRef keyRef
= (CFStringRef
)key
;
1121 //CFStringRef valueRef = (CFStringRef)value;
1122 bundleData
->dependencies
.push_back(CFStringGetCStringPtr(keyRef
, kCFStringEncodingASCII
));
1126 // Make sure no-one tries to link the kernel directly. They must do so via symbol sets
1127 if ( !bundleData
.dependencies
.empty() ) {
1128 for (const std::string
& dep
: bundleData
.dependencies
) {
1129 if (dep
== "com.apple.kernel") {
1130 fprintf(stderr
, "Rejecting bundle '%s' as it is trying to link directly to the kernel\n",
1138 bundleData
.executablePath
= bundleExecutablePath
;
1140 CFURLRef bundleURLRef
= CFBundleCopyBundleURL(bundleRef
);
1141 CFStringRef bundleURLString
= CFURLCopyFileSystemPath(bundleURLRef
, kCFURLPOSIXPathStyle
);
1142 const char* bundleURLPath
= CFStringGetCStringPtr(bundleURLString
, kCFStringEncodingASCII
);
1143 if (strncmp(bundleURLPath
, options
.extensionsPath
, strlen(options
.extensionsPath
)) != 0) {
1144 fprintf(stderr
, "Bundle path '%s' is not within extensions directory '%s'\n",
1145 bundleURLPath
, options
.extensionsPath
);
1147 // Don't remove the whole extensions prefix, but instead the volume root, if we have one
1148 bundleData
.bundlePath
= bundleURLPath
+ strlen(options
.volumeRoot
);
1149 foundBundles
[bundleName
] = bundleData
;
1151 CFRelease(bundleExecutableString
);
1152 CFRelease(bundleExecutableAbsoluteURL
);
1153 CFRelease(bundleExecutableRelativeURL
);
1160 __block
std::set
<std::string
> existingBundles
;
1161 auto addSymbolSetsBundleIDs
= ^(const dyld3::MachOAnalyzer
* kernelMA
) {
1162 assert(kernelMA
!= nullptr);
1164 __block
std::list
<std::string
> nonASCIIStrings
;
1165 auto getString
= ^(Diagnostics
& diags
, CFStringRef symbolNameRef
) {
1166 const char* symbolName
= CFStringGetCStringPtr(symbolNameRef
, kCFStringEncodingUTF8
);
1167 if ( symbolName
!= nullptr )
1170 CFIndex len
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(symbolNameRef
), kCFStringEncodingUTF8
);
1171 char buffer
[len
+ 1];
1172 if ( !CFStringGetCString(symbolNameRef
, buffer
, len
, kCFStringEncodingUTF8
) ) {
1173 diags
.error("Could not convert string to ASCII");
1174 return (const char*)nullptr;
1177 nonASCIIStrings
.push_back(buffer
);
1178 return nonASCIIStrings
.back().c_str();
1181 uint64_t symbolSetsSize
= 0;
1182 const void* symbolSetsContent
= kernelMA
->findSectionContent("__LINKINFO", "__symbolsets", symbolSetsSize
);
1183 if ( symbolSetsContent
!= nullptr ) {
1185 // A helper to automatically call CFRelease when we go out of scope
1186 struct AutoReleaseTypeRef
{
1187 AutoReleaseTypeRef() = default;
1188 ~AutoReleaseTypeRef() {
1189 if ( ref
!= nullptr ) {
1193 void setRef(CFTypeRef typeRef
) {
1194 assert(ref
== nullptr);
1198 CFTypeRef ref
= nullptr;
1201 AutoReleaseTypeRef dataRefReleaser
;
1202 AutoReleaseTypeRef plistRefReleaser
;
1204 CFDataRef dataRef
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, (const uint8_t*)symbolSetsContent
, symbolSetsSize
, kCFAllocatorNull
);
1205 if ( dataRef
== nullptr ) {
1206 diag
.error("Could not create data ref for symbol sets");
1209 dataRefReleaser
.setRef(dataRef
);
1211 CFErrorRef errorRef
= nullptr;
1212 CFPropertyListRef plistRef
= CFPropertyListCreateWithData(kCFAllocatorDefault
, dataRef
, kCFPropertyListImmutable
, nullptr, &errorRef
);
1213 if (errorRef
!= nullptr) {
1214 CFStringRef errorString
= CFErrorCopyDescription(errorRef
);
1215 diag
.error("Could not load plist because :%s",
1216 CFStringGetCStringPtr(errorString
, kCFStringEncodingASCII
));
1217 CFRelease(errorRef
);
1220 if ( plistRef
== nullptr ) {
1221 diag
.error("Could not create plist ref for symbol sets");
1224 plistRefReleaser
.setRef(plistRef
);
1226 if ( CFGetTypeID(plistRef
) != CFDictionaryGetTypeID() ) {
1227 diag
.error("Symbol set plist should be a dictionary");
1230 CFDictionaryRef symbolSetsDictRef
= (CFDictionaryRef
)plistRef
;
1231 CFArrayRef symbolSetArrayRef
= (CFArrayRef
)CFDictionaryGetValue(symbolSetsDictRef
, CFSTR("SymbolsSets"));
1232 if ( symbolSetArrayRef
!= nullptr ) {
1233 if ( CFGetTypeID(symbolSetArrayRef
) != CFArrayGetTypeID() ) {
1234 diag
.error("SymbolsSets value should be an array");
1237 for (CFIndex symbolSetIndex
= 0; symbolSetIndex
!= CFArrayGetCount(symbolSetArrayRef
); ++symbolSetIndex
) {
1238 CFDictionaryRef symbolSetDictRef
= (CFDictionaryRef
)CFArrayGetValueAtIndex(symbolSetArrayRef
, symbolSetIndex
);
1239 if ( CFGetTypeID(symbolSetDictRef
) != CFDictionaryGetTypeID() ) {
1240 diag
.error("Symbol set element should be a dictionary");
1244 // CFBundleIdentifier
1245 CFStringRef bundleIDRef
= (CFStringRef
)CFDictionaryGetValue(symbolSetDictRef
, CFSTR("CFBundleIdentifier"));
1246 if ( (bundleIDRef
== nullptr) || (CFGetTypeID(bundleIDRef
) != CFStringGetTypeID()) ) {
1247 diag
.error("Symbol set bundle ID should be a string");
1251 const char* dylibID
= getString(diag
, bundleIDRef
);
1252 if ( dylibID
== nullptr )
1254 existingBundles
.insert(dylibID
);
1261 auto addExistingBundleIDs
= ^(const char* path
, bool isBaseKC
) {
1262 char fileRealPath
[MAXPATHLEN
];
1263 auto kernelCollectionLoadedFileInfo
= MachOAnalyzer::load(diag
, fileSystem
, path
, archs
, platform
, fileRealPath
);
1264 if ( diag
.hasError() ) {
1265 fprintf(stderr
, "Could not load file '%s' because: %s\n", path
, diag
.errorMessage().c_str());
1268 const MachOAppCache
* appCacheMA
= (const MachOAppCache
*)kernelCollectionLoadedFileInfo
.fileContent
;
1269 if (appCacheMA
== nullptr) {
1270 fprintf(stderr
, "Could not load file: %s\n", path
);
1273 if ( !appCacheMA
->isFileSet() ) {
1274 fprintf(stderr
, "kernel collection is not a cache file: %s\n", path
);
1277 __block
const dyld3::MachOAnalyzer
* kernelMA
= nullptr;
1278 appCacheMA
->forEachDylib(diag
, ^(const MachOAnalyzer
*ma
, const char *name
, bool &stop
) {
1279 if ( ma
->isStaticExecutable() ) {
1282 existingBundles
.insert(name
);
1286 if ( !addSymbolSetsBundleIDs(kernelMA
) )
1289 fileSystem
.unloadFile(kernelCollectionLoadedFileInfo
);
1292 if ( options
.collectionKind
== baseKC
) {
1293 char fileRealPath
[MAXPATHLEN
];
1294 auto kernelLoadedFileInfo
= MachOAnalyzer::load(diag
, fileSystem
, options
.kernelPath
, archs
, platform
, fileRealPath
);
1295 if ( diag
.hasError() ) {
1296 fprintf(stderr
, "Could not load file '%s' because: %s\n", options
.kernelPath
, diag
.errorMessage().c_str());
1299 const MachOAppCache
* kernelMA
= (const MachOAppCache
*)kernelLoadedFileInfo
.fileContent
;
1300 if (kernelMA
== nullptr) {
1301 fprintf(stderr
, "Could not load file: %s\n", options
.kernelPath
);
1304 if ( !kernelMA
->isStaticExecutable() ) {
1305 fprintf(stderr
, "kernel is not a static executable: %s\n", options
.kernelPath
);
1309 if ( !addSymbolSetsBundleIDs(kernelMA
) )
1311 fileSystem
.unloadFile(kernelLoadedFileInfo
);
1313 if ( (options
.collectionKind
== auxKC
) || (options
.collectionKind
== pageableKC
) ) {
1314 // Work out which bundle-ids are already in the base KC
1315 if ( !addExistingBundleIDs(options
.kernelCollectionPath
, true) )
1318 if ( options
.pageableCollectionPath
!= nullptr ) {
1319 // Work out which bundle-ids are already in the pageable KC
1320 if ( !addExistingBundleIDs(options
.pageableCollectionPath
, false) )
1324 std::set
<std::string
> processedBundleIDs
;
1325 std::list
<const char*> bundleIDsToLoad
;
1326 bundleIDsToLoad
.insert(bundleIDsToLoad
.end(), options
.bundleIDs
.begin(), options
.bundleIDs
.end());
1327 while (!bundleIDsToLoad
.empty()) {
1328 std::string bundleID
= bundleIDsToLoad
.front();
1329 bundleIDsToLoad
.pop_front();
1331 std::string stripModeString
;
1332 if ( const char* colonPos
= strchr(bundleID
.c_str(), ':') ) {
1333 stripModeString
= colonPos
+ 1;
1334 bundleID
.erase(colonPos
- bundleID
.data());
1337 // If we've seen this one already then skip it
1338 if (!processedBundleIDs
.insert(bundleID
).second
)
1341 // Find the bundle for this ID
1342 auto it
= foundBundles
.find(bundleID
);
1343 if (it
== foundBundles
.end()) {
1344 fprintf(stderr
, "[WARNING]: Could not find bundle with ID '%s'\n", bundleID
.c_str());
1348 BundleData
& bundleData
= it
->second
;
1350 LoadedFileInfo info
;
1352 // Codeless kexts don't have an executable path, but we still want to put their
1353 // plist in the prelink info
1354 bool isCodeless
= bundleData
.executablePath
.empty();
1355 if ( !isCodeless
) {
1356 char realerPath
[MAXPATHLEN
];
1357 bool loadedFile
= fileSystem
.loadFile(bundleData
.executablePath
.c_str(), info
, realerPath
,
1358 ^(const char *format
, ...) {
1360 va_start(list
, format
);
1361 diag
.error(format
, list
);
1368 std::vector
<const char*> deps
;
1369 for (const std::string
& dependency
: bundleData
.dependencies
)
1370 deps
.push_back(dependency
.c_str());
1372 CFStringRef kextPathStringRef
= nullptr;
1373 CFDataRef kextDataRef
= nullptr;
1375 kextPathStringRef
= CFStringCreateWithCString(kCFAllocatorDefault
, bundleData
.executablePath
.c_str(), kCFStringEncodingASCII
);
1376 kextDataRef
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, (const uint8_t*)info
.fileContent
, info
.fileContentLen
, kCFAllocatorNull
);
1379 CFMutableArrayRef kextDepsArrayRef
= CFArrayCreateMutable(kCFAllocatorDefault
, bundleData
.dependencies
.size(), &kCFTypeArrayCallBacks
);
1380 for (const std::string
& dependency
: bundleData
.dependencies
) {
1381 CFStringRef depStringRef
= CFStringCreateWithCString(kCFAllocatorDefault
, dependency
.c_str(), kCFStringEncodingASCII
);
1382 CFArrayAppendValue(kextDepsArrayRef
, depStringRef
);
1383 CFRelease(depStringRef
);
1385 CFStringRef kextBundleIDStringRef
= CFStringCreateWithCString(kCFAllocatorDefault
, bundleID
.c_str(), kCFStringEncodingASCII
);
1386 CFStringRef kextBundlePathStringRef
= CFStringCreateWithCString(kCFAllocatorDefault
, bundleData
.bundlePath
.c_str(), kCFStringEncodingASCII
);
1388 BinaryStripMode stripMode
= binaryStripNone
;
1389 if ( !stripModeString
.empty() ) {
1390 if ( stripModeString
== "locals" ) {
1391 stripMode
= binaryStripLocals
;
1392 } else if ( stripModeString
== "exports" ) {
1393 stripMode
= binaryStripExports
;
1394 } else if ( stripModeString
== "all" ) {
1395 stripMode
= binaryStripAll
;
1397 diag
.error("Unknown strip mode: '%s'", stripModeString
.c_str());
1402 KextFileData_v1 fileData
= { 1, kextPathStringRef
, kextDataRef
,
1403 kextDepsArrayRef
, kextBundleIDStringRef
, kextBundlePathStringRef
,
1404 bundleData
.infoPlist
, stripMode
};
1406 if ( !addKextDataFile(kcb
, &fileData
) ) {
1407 uint64_t errorCount
= 0;
1408 const char* const* errors
= getErrors(kcb
, &errorCount
);
1409 for (uint64_t i
= 0; i
!= errorCount
; ++i
)
1410 diag
.error("Could not load kext file because: '%s'", errors
[i
]);
1414 // Walk the dependencies and add any new ones to the list
1415 for (const std::string
& dependency
: bundleData
.dependencies
) {
1416 if ( existingBundles
.find(dependency
) == existingBundles
.end() )
1417 bundleIDsToLoad
.push_back(dependency
.c_str());
1421 // Filter dependencies to kext's with binaries
1423 const std::map
<std::string
, BundleData
>* foundBundlesPtr
= &foundBundles
;
1424 for (AppCacheBuilder::InputDylib
& file
: loadedFiles
) {
1425 file
.dylibDeps
.erase(std::remove_if(file
.dylibDeps
.begin(), file
.dylibDeps
.end(),
1426 [&](const std::string
& depName
) {
1427 auto it
= foundBundlesPtr
->find(depName
);
1428 assert(it
!= foundBundlesPtr
->end());
1429 return it
->second
.executablePath
.empty();
1430 }),file
.dylibDeps
.end());
1436 for (AppCacheBuilder::InputDylib
& file
: loadedFiles
) {
1437 char fileRealPath
[MAXPATHLEN
];
1438 const char* path
= file
.dylib
.loadedFileInfo
.path
;
1439 LoadedFileInfo loadedFileInfo
= MachOAnalyzer::load(diag
, fileSystem
, path
, archs
, platform
, fileRealPath
);
1440 if ( diag
.hasError() ) {
1441 fprintf(stderr
, "Could not load file '%s' because: %s\n", path
, diag
.errorMessage().c_str());
1445 MachOAnalyzer
* ma
= (MachOAnalyzer
*)loadedFileInfo
.fileContent
;
1446 if (ma
== nullptr) {
1447 fprintf(stderr
, "Could not load file: %s\n", path
);
1451 auto errorHandler
= ^(const char* msg
) {
1452 diag
.error("Binary located at '%s' cannot be placed in kernel collection because: %s", path
, msg
);
1454 if (ma
->canBePlacedInKernelCollection(path
, errorHandler
)) {
1455 DyldSharedCache::MappedMachO
mappedFile(path
, ma
, loadedFileInfo
.sliceLen
, false, false,
1456 loadedFileInfo
.sliceOffset
, loadedFileInfo
.mtime
,
1457 loadedFileInfo
.inode
);
1458 CacheBuilder::LoadedMachO loadedMachO
= { mappedFile
, loadedFileInfo
, nullptr };
1459 file
.dylib
= loadedMachO
;
1461 fileSystem
.unloadFile(loadedFileInfo
);
1463 if ( diag
.hasError() ) {
1464 fprintf(stderr
, "%s\n", diag
.errorMessage().c_str());
1471 if (loadedFiles
.empty()) {
1472 fprintf(stderr
, "Could not find any valid files to create kernel collection\n");
1474 // Since we found no files, print warnings for the ones we tried
1475 if (!diag
.warnings().empty()) {
1476 fprintf(stderr
, "Failed to use the following files:\n");
1477 for (const std::string
& msg
: diag
.warnings()) {
1478 fprintf(stderr
, " %s\n", msg
.c_str());
1484 if (options
.verbose
) {
1485 for (const AppCacheBuilder::InputDylib
& loadedFile
: loadedFiles
)
1486 fprintf(stderr
, "Building cache with file: %s\n", loadedFile
.dylib
.loadedFileInfo
.path
);
1490 for (const SectionData
& sectData
: options
.sections
) {
1491 CFStringRef segmentName
= CFStringCreateWithCString(kCFAllocatorDefault
, sectData
.segmentName
, kCFStringEncodingASCII
);
1492 CFStringRef sectionName
= nullptr;
1493 if ( sectData
.sectionName
!= nullptr )
1494 sectionName
= CFStringCreateWithCString(kCFAllocatorDefault
, sectData
.sectionName
, kCFStringEncodingASCII
);
1496 CFDataRef sectionData
= nullptr;
1498 struct stat stat_buf
;
1499 int fd
= ::open(sectData
.payloadFilePath
, O_RDONLY
, 0);
1501 diag
.error("can't open file '%s', errno=%d\n", sectData
.payloadFilePath
, errno
);
1505 if (fstat(fd
, &stat_buf
) == -1) {
1506 diag
.error("can't stat open file '%s', errno=%d\n", sectData
.payloadFilePath
, errno
);
1511 const void* buffer
= mmap(NULL
, (size_t)stat_buf
.st_size
, PROT_READ
, MAP_PRIVATE
, fd
, 0);
1512 if (buffer
== MAP_FAILED
) {
1513 diag
.error("mmap() for file at %s failed, errno=%d\n", sectData
.payloadFilePath
, errno
);
1519 sectionData
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, (const uint8_t*)buffer
, stat_buf
.st_size
, kCFAllocatorNull
);
1522 if ( !addSegmentData(kcb
, segmentName
, sectionName
, sectionData
) ) {
1523 uint64_t errorCount
= 0;
1524 const char* const* errors
= getErrors(kcb
, &errorCount
);
1525 for (uint64_t i
= 0; i
!= errorCount
; ++i
)
1526 diag
.error("Could not load section data file because: '%s'", errors
[i
]);
1531 if ( options
.prelinkInfoExtraData
!= nullptr ) {
1532 struct stat stat_buf
;
1533 int fd
= ::open(options
.prelinkInfoExtraData
, O_RDONLY
, 0);
1535 diag
.error("can't open file '%s', errno=%d\n", options
.prelinkInfoExtraData
, errno
);
1539 if (fstat(fd
, &stat_buf
) == -1) {
1540 diag
.error("can't stat open file '%s', errno=%d\n", options
.prelinkInfoExtraData
, errno
);
1545 const void* buffer
= mmap(NULL
, (size_t)stat_buf
.st_size
, PROT_READ
, MAP_PRIVATE
, fd
, 0);
1546 if (buffer
== MAP_FAILED
) {
1547 diag
.error("mmap() for file at %s failed, errno=%d\n", options
.prelinkInfoExtraData
, errno
);
1553 CFDataRef prelinkInfoData
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, (const uint8_t*)buffer
, stat_buf
.st_size
, kCFAllocatorNull
);
1555 CFErrorRef errorRef
= nullptr;
1556 CFPropertyListRef plistRef
= CFPropertyListCreateWithData(kCFAllocatorDefault
, prelinkInfoData
, kCFPropertyListImmutable
, nullptr, &errorRef
);
1557 if (errorRef
!= nullptr) {
1558 CFStringRef errorString
= CFErrorCopyDescription(errorRef
);
1559 diag
.error("Could not load prelink info plist because :%s",
1560 CFStringGetCStringPtr(errorString
, kCFStringEncodingASCII
));
1561 CFRelease(errorRef
);
1564 if ( plistRef
== nullptr ) {
1565 diag
.error("Could not create plist ref for prelink info");
1568 if ( CFGetTypeID(plistRef
) != CFDictionaryGetTypeID() ) {
1569 diag
.error("Prelink info plist should be a dictionary");
1573 if ( !addPrelinkInfo(kcb
, (CFDictionaryRef
)plistRef
) ) {
1574 uint64_t errorCount
= 0;
1575 const char* const* errors
= getErrors(kcb
, &errorCount
);
1576 for (uint64_t i
= 0; i
!= errorCount
; ++i
)
1577 diag
.error("Could not prelink data file because: '%s'", errors
[i
]);
1582 bool success
= runKernelCollectionBuilder(kcb
);
1583 uint64_t errorCount
= 0;
1584 const char* const* errors
= getErrors(kcb
, &errorCount
);
1585 if ( errors
!= nullptr ) {
1586 if ( !options
.printJSONErrors
) {
1587 for (uint64_t i
= 0; i
!= errorCount
; ++i
) {
1588 fprintf(stderr
, "Could not build kernel collection because '%s'\n", errors
[i
]);
1591 CFDictionaryRef errorDictRef
= getKextErrors(kcb
);
1592 if ( errorDictRef
!= nullptr ) {
1595 CFDictionaryApplyFunction(errorDictRef
, [](const void *key
, const void *value
, void *context
) {
1596 Node
* rootNode
= (Node
*)context
;
1597 CFStringRef keyRef
= (CFStringRef
)key
;
1598 CFArrayRef valueRef
= (CFArrayRef
)value
;
1601 bundleNode
.map
["id"] = Node(CFStringGetCStringPtr(keyRef
, kCFStringEncodingASCII
));
1604 CFArrayApplyFunction(valueRef
, CFRangeMake(0, CFArrayGetCount(valueRef
)), [](const void *value
, void *context
) {
1605 Node
* errorsNode
= (Node
*)context
;
1606 CFStringRef valueRef
= (CFStringRef
)value
;
1608 errorsNode
->array
.push_back(Node(CFStringGetCStringPtr(valueRef
, kCFStringEncodingASCII
)));
1611 bundleNode
.map
["errors"] = errorsNode
;
1613 rootNode
->array
.push_back(bundleNode
);
1616 // sort the nodes so that the output is reproducible
1617 std::sort(rootNode
.array
.begin(), rootNode
.array
.end(),
1618 [](const Node
& a
, const Node
&b
) {
1619 return a
.map
.find("id")->second
.value
< b
.map
.find("id")->second
.value
;
1622 printJSON(rootNode
);
1625 for (uint64_t i
= 0; i
!= errorCount
; ++i
) {
1626 rootNode
.array
.push_back(Node(errors
[i
]));
1628 printJSON(rootNode
);
1636 uint64_t fileResultCount
= 0;
1637 const auto* fileResults
= getCollectionFileResults(kcb
, &fileResultCount
);
1638 if ( fileResults
== nullptr ) {
1639 fprintf(stderr
, "Could not get file results\n");
1642 if ( fileResultCount
!= 1 ) {
1643 fprintf(stderr
, "Unsupported file result count: %lld\n", fileResultCount
);
1647 CFDataRef dataRef
= fileResults
[0]->data
;
1650 destroyKernelCollectionBuilder(kcb
);
1655 static int createKernelCollection(const CreateKernelCollectionOptions
& options
) {
1656 // Verify any required options
1657 if (gOpts
.archs
.empty()) {
1658 exit_usage("-arch");
1660 std::set
<std::string_view
> archs(gOpts
.archs
.begin(), gOpts
.archs
.end());
1661 if (archs
.size() != gOpts
.archs
.size()) {
1662 fprintf(stderr
, "Duplicate -arch specified\n");
1666 if (options
.outputCachePath
== nullptr)
1669 switch (options
.stripMode
) {
1670 case unknownStripMode
:
1675 if ( options
.collectionKind
!= baseKC
) {
1676 fprintf(stderr
, "Cannot use -strip-all-kexts with auxKC. Use strip-all instead\n");
1682 switch (options
.collectionKind
) {
1684 fprintf(stderr
, "Invalid kernel collection kind\n");
1687 if (options
.kernelPath
== nullptr)
1688 exit_usage("-kernel");
1692 if (options
.kernelCollectionPath
== nullptr)
1693 exit_usage("-kernel-collection");
1697 if ( !options
.bundleIDs
.empty() ) {
1698 if (options
.extensionsPath
== nullptr)
1699 exit_usage("-extensions");
1702 // Volume root should be a prefix of extensions path
1703 if ( options
.extensionsPath
!= nullptr ) {
1704 if ( strncmp(options
.extensionsPath
, options
.volumeRoot
, strlen(options
.volumeRoot
)) != 0 ) {
1705 fprintf(stderr
, "Volume root '%s' is not a prefix of extensions path '%s'\n",
1706 options
.volumeRoot
, options
.extensionsPath
);
1710 std::vector
<CFDataRef
> buffers
;
1711 for (const char* arch
: gOpts
.archs
) {
1713 CFDataRef bufferRef
= createKernelCollectionForArch(options
, arch
, diag
);
1714 if ( diag
.hasError() ) {
1715 fprintf(stderr
, "%s\n", diag
.errorMessage().c_str());
1718 if ( bufferRef
== nullptr ) {
1719 // If we want errors then return 0
1720 if ( options
.printJSONErrors
)
1724 buffers
.push_back(bufferRef
);
1727 if (buffers
.size() == 1) {
1728 // Single arch. Just write the file directly
1729 CFDataRef bufferRef
= buffers
.front();
1730 if ( !safeSave(CFDataGetBytePtr(bufferRef
), CFDataGetLength(bufferRef
), options
.outputCachePath
) ) {
1731 fprintf(stderr
, "Could not write app cache\n");
1734 CFRelease(bufferRef
);
1736 // Multiple buffers. Create a FAT file
1737 std::vector
<uint8_t> fatBuffer
;
1739 // Add the FAT header to the start of the buffer
1740 fatBuffer
.resize(0x4000, 0);
1741 fat_header
* header
= (fat_header
*)&fatBuffer
.front();
1742 header
->magic
= OSSwapHostToBigInt32(FAT_MAGIC
);
1743 header
->nfat_arch
= OSSwapHostToBigInt32((uint32_t)buffers
.size());
1745 for (uint32_t i
= 0; i
!= buffers
.size(); ++i
) {
1746 CFDataRef bufferRef
= buffers
[i
];
1747 mach_header
* mh
= (mach_header
*)CFDataGetBytePtr(bufferRef
);
1749 uint32_t offsetInBuffer
= (uint32_t)fatBuffer
.size();
1751 fat_arch
* archBuffer
= (fat_arch
*)(&fatBuffer
.front() + sizeof(fat_header
));
1752 archBuffer
[i
].cputype
= OSSwapHostToBigInt32(mh
->cputype
);
1753 archBuffer
[i
].cpusubtype
= OSSwapHostToBigInt32(mh
->cpusubtype
);
1754 archBuffer
[i
].offset
= OSSwapHostToBigInt32(offsetInBuffer
);
1755 archBuffer
[i
].size
= OSSwapHostToBigInt32((uint32_t)CFDataGetLength(bufferRef
));
1756 archBuffer
[i
].align
= OSSwapHostToBigInt32(14);
1758 auto align
= [](uint64_t addr
, uint8_t p2
) {
1759 uint64_t mask
= (1 << p2
);
1760 return (addr
+ mask
- 1) & (-mask
);
1763 uint32_t alignedSize
= (uint32_t)align((uint32_t)CFDataGetLength(bufferRef
), 14);
1764 fatBuffer
.resize(fatBuffer
.size() + alignedSize
, 0);
1765 memcpy(&fatBuffer
.front() + offsetInBuffer
, CFDataGetBytePtr(bufferRef
), CFDataGetLength(bufferRef
));
1768 if ( !safeSave(&fatBuffer
.front(), fatBuffer
.size(), options
.outputCachePath
) ) {
1769 fprintf(stderr
, "Could not write app cache\n");
1777 int main(int argc
, const char* argv
[]) {
1778 OptionsVariants options
;
1779 if (!parseArgs(argc
, argv
, options
))
1782 if (std::holds_alternative
<DumpOptions
>(options
)) {
1783 return dumpAppCache(std::get
<DumpOptions
>(options
));
1786 if (std::holds_alternative
<ValidateOptions
>(options
)) {
1787 return validateFile(std::get
<ValidateOptions
>(options
));
1790 if (std::holds_alternative
<ListBundlesOptions
>(options
)) {
1791 return listBundles(std::get
<ListBundlesOptions
>(options
));
1794 if (std::holds_alternative
<CreateKernelCollectionOptions
>(options
)) {
1795 return createKernelCollection(std::get
<CreateKernelCollectionOptions
>(options
));
1798 assert(std::holds_alternative
<std::monostate
>(options
));