1 /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
3 * Copyright (c) 2017 Apple Inc. All rights reserved.
5 * @APPLE_LICENSE_HEADER_START@
7 * This file contains Original Code and/or Modifications of Original Code
8 * as defined in and that are subject to the Apple Public Source License
9 * Version 2.0 (the 'License'). You may not use this file except in
10 * compliance with the License. Please obtain a copy of the License at
11 * http://www.opensource.apple.com/apsl/ and read it before using this
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
19 * Please see the License for the specific language governing rights and
20 * limitations under the License.
22 * @APPLE_LICENSE_HEADER_END@
25 #include "kernel_collection_builder.h"
27 #include "AppCacheBuilder.h"
28 #include "Diagnostics.h"
29 #include "ClosureFileSystemNull.h"
30 #include "MachOAppCache.h"
36 static const uint64_t kMinBuildVersion
= 1; //The minimum version BuildOptions struct we can support
37 static const uint64_t kMaxBuildVersion
= 1; //The maximum version BuildOptions struct we can support
39 static const uint32_t MajorVersion
= 1;
40 static const uint32_t MinorVersion
= 0;
42 struct KernelCollectionBuilder
{
44 KernelCollectionBuilder(const BuildOptions_v1
* options
);
46 struct CollectionFile
{
47 const uint8_t* data
= nullptr;
49 const char* path
= nullptr;
53 uint8_t* buffer
= nullptr;
54 uint64_t bufferSize
= 0;
57 const char* arch
= "";
58 BuildOptions_v1 options
;
59 std::list
<Diagnostics
> inputFileDiags
;
60 std::vector
<AppCacheBuilder::InputDylib
> inputFiles
;
61 dyld3::closure::LoadedFileInfo kernelCollectionFileInfo
;
62 dyld3::closure::LoadedFileInfo pageableCollectionFileInfo
;
63 std::vector
<AppCacheBuilder::CustomSegment
> customSections
;
64 CFDictionaryRef prelinkInfoExtraData
= nullptr;
66 std::list
<CollectionFileResult_v1
> fileResultsStorage
;
67 std::vector
<const CollectionFileResult_v1
*> fileResults
;
68 CFDictionaryRef errorsDict
= nullptr;
70 std::vector
<const char*> errors
;
71 std::vector
<std::string
> errorStorage
;
73 std::list
<std::string
> duplicatedStrings
;
74 std::vector
<CFTypeRef
> typeRefs
;
76 __attribute__((format(printf
, 2, 3)))
77 void error(const char* format
, ...) {
79 va_start(list
, format
);
81 diag
.error(format
, list
);
84 errorStorage
.push_back(diag
.errorMessage());
85 errors
.push_back(errorStorage
.back().data());
88 void retain(CFTypeRef v
) {
90 typeRefs
.push_back(v
);
93 const char* strdup(CFStringRef str
) {
94 size_t length
= CFStringGetLength(str
);
95 char buffer
[length
+ 1];
96 memset(&buffer
[0], 0, length
+ 1);
97 if ( !CFStringGetCString(str
, buffer
, length
+ 1, kCFStringEncodingASCII
) ) {
98 error("Could not convert to ASCII");
101 duplicatedStrings
.push_back(buffer
);
102 return duplicatedStrings
.back().c_str();
107 // What is the version of this builder dylib. Can be used to determine what API is available.
108 void getVersion(uint32_t *major
, uint32_t *minor
) {
109 *major
= MajorVersion
;
110 *minor
= MinorVersion
;
113 KernelCollectionBuilder::KernelCollectionBuilder(const BuildOptions_v1
* options
)
114 : options(*options
) {
115 retain(this->options
.arch
);
118 // Returns a valid object on success, or NULL on failure.
119 __API_AVAILABLE(macos(10.14))
120 struct KernelCollectionBuilder
* createKernelCollectionBuilder(const struct BuildOptions_v1
* options
) {
121 KernelCollectionBuilder
* builder
= new KernelCollectionBuilder(options
);
123 if (options
->version
< kMinBuildVersion
) {
124 builder
->error("Builder version %llu is less than minimum supported version of %llu", options
->version
, kMinBuildVersion
);
127 if (options
->version
> kMaxBuildVersion
) {
128 builder
->error("Builder version %llu is greater than maximum supported version of %llu", options
->version
, kMaxBuildVersion
);
131 if ( options
->arch
== nullptr ) {
132 builder
->error("arch must not be null");
135 const char* archName
= builder
->strdup(options
->arch
);
136 if ( archName
== nullptr ) {
137 // Already generated an error in strdup.
140 builder
->arch
= archName
;
145 static bool loadFileFromData(struct KernelCollectionBuilder
* builder
,
146 const CFStringRef path
, const CFDataRef data
,
147 dyld3::closure::LoadedFileInfo
& fileInfo
) {
148 fileInfo
.fileContent
= CFDataGetBytePtr(data
);
149 fileInfo
.fileContentLen
= CFDataGetLength(data
);
150 fileInfo
.sliceOffset
= 0;
151 fileInfo
.sliceLen
= CFDataGetLength(data
);
152 fileInfo
.isOSBinary
= false;
155 fileInfo
.unload
= nullptr;
156 fileInfo
.path
= builder
->strdup(path
);
159 dyld3::closure::FileSystemNull fileSystem
;
160 const dyld3::GradedArchs
& arch
= dyld3::GradedArchs::forName(builder
->arch
);
161 auto loaded
= dyld3::MachOAnalyzer::loadFromBuffer(diag
, fileSystem
, fileInfo
.path
, arch
,
162 dyld3::Platform::unknown
, fileInfo
);
164 builder
->error("%s", diag
.errorMessage().c_str());
171 // Add a kernel static executable file. Returns true on success.
172 __API_AVAILABLE(macos(10.14))
173 bool addKernelFile(struct KernelCollectionBuilder
* builder
, const CFStringRef path
, const CFDataRef data
) {
174 builder
->retain(path
);
175 builder
->retain(data
);
177 dyld3::closure::LoadedFileInfo info
;
178 bool loaded
= loadFileFromData(builder
, path
, data
, info
);
182 DyldSharedCache::MappedMachO
mappedFile(info
.path
, (const dyld3::MachOAnalyzer
*)info
.fileContent
,
183 info
.fileContentLen
, false, false,
184 info
.sliceOffset
, info
.mtime
, info
.inode
);
186 AppCacheBuilder::InputDylib input
;
187 input
.dylib
.mappedFile
= mappedFile
;
188 input
.dylib
.loadedFileInfo
= info
;
189 input
.dylib
.inputFile
= nullptr;
190 input
.dylibID
= "com.apple.kernel";
191 input
.errors
= &builder
->inputFileDiags
.emplace_back();
193 builder
->inputFiles
.push_back(input
);
197 // Add kext mach-o and plist files. Returns true on success.
198 __API_AVAILABLE(macos(10.14))
199 bool addKextDataFile(struct KernelCollectionBuilder
* builder
, const KextFileData_v1
* fileData
) {
200 if (fileData
->version
!= 1) {
201 builder
->error("addKextDataFile version %llu is less than minimum supported version of %d", fileData
->version
, 1);
205 builder
->retain(fileData
->dependencies
);
206 builder
->retain(fileData
->bundleID
);
207 builder
->retain(fileData
->bundlePath
);
208 builder
->retain(fileData
->plist
);
210 dyld3::closure::LoadedFileInfo info
;
212 // Code-less kext's don't have a mach-o to load
213 const char* kextpath
= nullptr;
214 if ( fileData
->kextdata
!= nullptr ) {
215 builder
->retain(fileData
->kextpath
);
216 builder
->retain(fileData
->kextdata
);
217 bool loaded
= loadFileFromData(builder
, fileData
->kextpath
, fileData
->kextdata
, info
);
220 kextpath
= info
.path
;
222 kextpath
= "codeless";
225 DyldSharedCache::MappedMachO
mappedFile(kextpath
, (const dyld3::MachOAnalyzer
*)info
.fileContent
,
226 info
.fileContentLen
, false, false,
227 info
.sliceOffset
, info
.mtime
, info
.inode
);
229 uint64_t numDependencies
= CFArrayGetCount(fileData
->dependencies
);
231 AppCacheBuilder::InputDylib input
;
232 input
.dylib
.mappedFile
= mappedFile
;
233 input
.dylib
.loadedFileInfo
= info
;
234 input
.dylib
.inputFile
= nullptr;
235 input
.dylibID
= builder
->strdup(fileData
->bundleID
);
236 for (uint64_t i
= 0; i
!= numDependencies
; ++i
) {
237 CFTypeRef elementRef
= CFArrayGetValueAtIndex(fileData
->dependencies
, i
);
238 if ( CFGetTypeID(elementRef
) != CFStringGetTypeID() ) {
239 builder
->error("Dependency %llu of %s is not a string", i
, info
.path
);
242 CFStringRef stringRef
= (CFStringRef
)elementRef
;
243 input
.dylibDeps
.push_back(builder
->strdup(stringRef
));
245 input
.infoPlist
= fileData
->plist
;
246 input
.errors
= &builder
->inputFileDiags
.emplace_back();
247 input
.bundlePath
= builder
->strdup(fileData
->bundlePath
);
248 switch ( fileData
->stripMode
) {
249 case binaryUnknownStripMode
:
250 input
.stripMode
= CacheBuilder::DylibStripMode::stripNone
;
252 case binaryStripNone
:
253 input
.stripMode
= CacheBuilder::DylibStripMode::stripNone
;
255 case binaryStripExports
:
256 input
.stripMode
= CacheBuilder::DylibStripMode::stripExports
;
258 case binaryStripLocals
:
259 input
.stripMode
= CacheBuilder::DylibStripMode::stripLocals
;
262 input
.stripMode
= CacheBuilder::DylibStripMode::stripAll
;
266 builder
->inputFiles
.push_back(input
);
270 // Add interface plist file. Returns true on success.
271 __API_AVAILABLE(macos(10.14))
272 bool addInterfaceFile(struct KernelCollectionBuilder
* builder
, const CFStringRef path
, const CFDataRef data
) {
273 builder
->retain(path
);
274 builder
->retain(data
);
279 // Add collection file. Returns true on success.
280 __API_AVAILABLE(macos(10.14))
281 bool addCollectionFile(struct KernelCollectionBuilder
* builder
, const CFStringRef path
, const CFDataRef data
,
282 CollectionKind kind
) {
283 dyld3::closure::LoadedFileInfo
* fileInfo
= nullptr;
284 if ( kind
== baseKC
) {
285 if ( (builder
->options
.collectionKind
!= auxKC
) && (builder
->options
.collectionKind
!= pageableKC
) ) {
286 builder
->error("Invalid collection file for build");
289 fileInfo
= &builder
->kernelCollectionFileInfo
;
290 } else if ( kind
== pageableKC
) {
291 if ( builder
->options
.collectionKind
!= auxKC
) {
292 builder
->error("Invalid collection file for build");
295 if ( builder
->pageableCollectionFileInfo
.fileContent
!= nullptr ) {
296 builder
->error("Already have collection file");
299 fileInfo
= &builder
->pageableCollectionFileInfo
;
301 builder
->error("Unsupported collection kind");
304 if ( fileInfo
->fileContent
!= nullptr ) {
305 builder
->error("Already have collection file");
309 builder
->retain(path
);
310 builder
->retain(data
);
312 bool loaded
= loadFileFromData(builder
, path
, data
, *fileInfo
);
316 const dyld3::MachOFile
* mf
= (const dyld3::MachOFile
*)fileInfo
->fileContent
;
317 if ( !mf
->isFileSet() ) {
318 builder
->error("kernel collection is not a cache file: %s\n", builder
->strdup(path
));
325 // Add data to the given segment of the final file. Note the section can be null (or "") if desired
326 __API_AVAILABLE(macos(10.14))
327 bool addSegmentData(struct KernelCollectionBuilder
* builder
, const CFStringRef segmentName
,
328 const CFStringRef sectionName
, const CFDataRef data
) {
329 // Check the segment name
330 if ( segmentName
== nullptr ) {
331 builder
->error("Segment data name must be non-null");
334 CFIndex segmentNameLength
= CFStringGetLength(segmentName
);
335 if ( (segmentNameLength
== 0) || (segmentNameLength
> 16) ) {
336 builder
->error("Segment data name must not be empty or > 16 characters");
340 AppCacheBuilder::CustomSegment::CustomSection section
;
342 // Check the section name
343 if ( sectionName
!= nullptr ) {
344 CFIndex sectionNameLength
= CFStringGetLength(sectionName
);
345 if ( sectionNameLength
!= 0 ) {
346 if ( sectionNameLength
> 16 ) {
347 builder
->error("Section data name must not be empty or > 16 characters");
350 section
.sectionName
= builder
->strdup(sectionName
);
355 if ( data
== nullptr ) {
356 builder
->error("Segment data payload must be non-null");
359 const uint8_t* dataStart
= CFDataGetBytePtr(data
);
360 const uint64_t dataLength
= CFDataGetLength(data
);
361 if ( dataLength
== 0 ) {
362 builder
->error("Segment data payload must not be empty");
366 builder
->retain(data
);
367 section
.data
.insert(section
.data
.end(), dataStart
, dataStart
+ dataLength
);
369 AppCacheBuilder::CustomSegment segment
;
370 segment
.segmentName
= builder
->strdup(segmentName
);
371 segment
.sections
.push_back(section
);
372 builder
->customSections
.push_back(segment
);
376 // Add data to the given segment of the final file. Note the section can be null (or "") if desired
377 __API_AVAILABLE(macos(10.14))
378 bool addPrelinkInfo(struct KernelCollectionBuilder
* builder
, const CFDictionaryRef extraData
) {
379 if ( builder
->prelinkInfoExtraData
!= nullptr ) {
380 builder
->error("Prelink info data has already been set by an earlier call");
384 if ( extraData
== nullptr ) {
385 builder
->error("Prelink info data payload must be non-null");
388 if ( CFDictionaryGetCount(extraData
) == 0 ) {
389 builder
->error("Prelink info data payload must not be empty");
392 builder
->retain(extraData
);
393 builder
->prelinkInfoExtraData
= extraData
;
397 // Set a handler to be called at various points during the build to notify the user of progress.
398 __API_AVAILABLE(macos(10.14))
399 void setProgressCallback(const ProgressCallback callback
) {
403 static AppCacheBuilder::Options::AppCacheKind
cacheKind(CollectionKind kind
) {
406 return AppCacheBuilder::Options::AppCacheKind::none
;
408 return AppCacheBuilder::Options::AppCacheKind::kernel
;
410 return AppCacheBuilder::Options::AppCacheKind::auxKC
;
412 return AppCacheBuilder::Options::AppCacheKind::pageableKC
;
416 static AppCacheBuilder::Options::StripMode
stripMode(StripMode mode
) {
418 case unknownStripMode
:
419 return AppCacheBuilder::Options::StripMode::none
;
421 return AppCacheBuilder::Options::StripMode::none
;
423 return AppCacheBuilder::Options::StripMode::all
;
425 return AppCacheBuilder::Options::StripMode::allExceptKernel
;
429 static void generatePerKextErrors(struct KernelCollectionBuilder
* builder
) {
430 CFMutableDictionaryRef errorsDict
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, nullptr, nullptr);
431 builder
->typeRefs
.push_back(errorsDict
);
433 for (const AppCacheBuilder::InputDylib
& file
: builder
->inputFiles
) {
434 if ( !file
.errors
->hasError() )
437 CFStringRef bundleID
= CFStringCreateWithCString(kCFAllocatorDefault
, file
.dylibID
.c_str(),
438 kCFStringEncodingASCII
);
439 builder
->typeRefs
.push_back(bundleID
);
441 CFMutableArrayRef errorsArray
= CFArrayCreateMutable(kCFAllocatorDefault
, 1, nullptr);
442 builder
->typeRefs
.push_back(errorsArray
);
444 CFStringRef errorString
= CFStringCreateWithCString(kCFAllocatorDefault
, file
.errors
->errorMessage().c_str(),
445 kCFStringEncodingASCII
);
446 builder
->typeRefs
.push_back(errorString
);
448 CFArrayAppendValue(errorsArray
, errorString
);
450 // Add this bundle to the dictionary
451 CFDictionarySetValue(errorsDict
, bundleID
, errorsArray
);
453 // FIXME: Remove this once the kernel linker has adopted the new API to get the per-kext errors
454 // For now also put the per-kext errors on the main error list
455 builder
->error("Could not use '%s' because: %s", file
.dylibID
.c_str(), file
.errors
->errorMessage().c_str());
458 if ( CFDictionaryGetCount(errorsDict
) != 0 ) {
459 builder
->errorsDict
= errorsDict
;
463 // Returns true on success.
464 __API_AVAILABLE(macos(10.14))
465 bool runKernelCollectionBuilder(struct KernelCollectionBuilder
* builder
) {
467 // Make sure specificed bundle-id's are not already in the caches we
469 __block
std::set
<std::string_view
> existingBundles
;
470 if ( (builder
->options
.collectionKind
== auxKC
) || (builder
->options
.collectionKind
== pageableKC
) ) {
471 if ( builder
->kernelCollectionFileInfo
.fileContent
== nullptr ) {
472 builder
->error("Cannot build pageableKC/auxKC without baseKC");
478 const dyld3::MachOAppCache
* kernelCacheMA
= (const dyld3::MachOAppCache
*)builder
->kernelCollectionFileInfo
.fileContent
;
479 kernelCacheMA
->forEachDylib(diag
, ^(const dyld3::MachOAnalyzer
*ma
, const char *name
, bool &stop
) {
480 existingBundles
.insert(name
);
483 // Check the pageableKC if we have one
484 const dyld3::MachOAppCache
* pageableCacheMA
= (const dyld3::MachOAppCache
*)builder
->kernelCollectionFileInfo
.fileContent
;
485 if ( pageableCacheMA
!= nullptr ) {
486 pageableCacheMA
->forEachDylib(diag
, ^(const dyld3::MachOAnalyzer
*ma
, const char *name
, bool &stop
) {
487 existingBundles
.insert(name
);
491 bool foundBadBundle
= false;
492 for (const AppCacheBuilder::InputDylib
& input
: builder
->inputFiles
) {
493 if ( existingBundles
.find(input
.dylibID
) != existingBundles
.end() ) {
494 builder
->error("kernel collection already contains bundle-id: %s\n", input
.dylibID
.c_str());
495 foundBadBundle
= true;
498 if ( foundBadBundle
)
502 dispatch_apply(builder
->inputFiles
.size(), DISPATCH_APPLY_AUTO
, ^(size_t index
) {
503 const AppCacheBuilder::InputDylib
& input
= builder
->inputFiles
[index
];
504 auto errorHandler
= ^(const char* msg
) {
505 input
.errors
->error("cannot be placed in kernel collection because: %s", msg
);
507 const dyld3::MachOAnalyzer
* ma
= (const dyld3::MachOAnalyzer
*)input
.dylib
.loadedFileInfo
.fileContent
;
508 // Skip codeless kexts
511 if (!ma
->canBePlacedInKernelCollection(input
.dylib
.loadedFileInfo
.path
, errorHandler
)) {
512 assert(input
.errors
->hasError());
515 for (const AppCacheBuilder::InputDylib
& input
: builder
->inputFiles
) {
516 if ( input
.errors
->hasError() ) {
517 builder
->error("One or more binaries has an error which prevented linking. See other errors.");
518 generatePerKextErrors(builder
);
523 DyldSharedCache::CreateOptions builderOptions
= {};
524 std::string runtimePath
= "";
525 builderOptions
.outputFilePath
= runtimePath
;
526 builderOptions
.outputMapFilePath
= builderOptions
.outputFilePath
+ ".json";
527 builderOptions
.archs
= &dyld3::GradedArchs::forName(builder
->arch
);
528 builderOptions
.platform
= dyld3::Platform::unknown
;
529 builderOptions
.localSymbolMode
= DyldSharedCache::LocalSymbolsMode::keep
;
530 builderOptions
.optimizeStubs
= true;
531 builderOptions
.optimizeDyldDlopens
= false;
532 builderOptions
.optimizeDyldLaunches
= false;
533 builderOptions
.codeSigningDigestMode
= DyldSharedCache::CodeSigningDigestMode::SHA256only
;
534 builderOptions
.dylibsRemovedDuringMastering
= true;
535 builderOptions
.inodesAreSameAsRuntime
= false;
536 builderOptions
.cacheSupportsASLR
= true;
537 builderOptions
.forSimulator
= false;
538 builderOptions
.isLocallyBuiltCache
= true;
539 builderOptions
.verbose
= builder
->options
.verboseDiagnostics
;
540 builderOptions
.evictLeafDylibsOnOverflow
= true;
541 builderOptions
.loggingPrefix
= "";
542 builderOptions
.dylibOrdering
= {};
543 builderOptions
.dirtyDataSegmentOrdering
= {};
544 builderOptions
.objcOptimizations
= {};
546 AppCacheBuilder::Options appCacheOptions
;
547 appCacheOptions
.cacheKind
= cacheKind(builder
->options
.collectionKind
);
548 appCacheOptions
.stripMode
= stripMode(builder
->options
.stripMode
);
550 const dyld3::closure::FileSystemNull builderFileSystem
;
552 AppCacheBuilder
cacheBuilder(builderOptions
, appCacheOptions
, builderFileSystem
);
553 if ( builder
->kernelCollectionFileInfo
.fileContent
!= nullptr ) {
554 const dyld3::MachOAppCache
* appCacheMA
= (const dyld3::MachOAppCache
*)builder
->kernelCollectionFileInfo
.fileContent
;
555 cacheBuilder
.setExistingKernelCollection(appCacheMA
);
557 if ( builder
->pageableCollectionFileInfo
.fileContent
!= nullptr ) {
558 const dyld3::MachOAppCache
* appCacheMA
= (const dyld3::MachOAppCache
*)builder
->pageableCollectionFileInfo
.fileContent
;
559 cacheBuilder
.setExistingPageableKernelCollection(appCacheMA
);
562 // Add custom sections
563 for (const AppCacheBuilder::CustomSegment
& segment
: builder
->customSections
) {
564 if ( !cacheBuilder
.addCustomSection(segment
.segmentName
, segment
.sections
.front()) ) {
565 builder
->error("%s", cacheBuilder
.errorMessage().c_str());
570 // Add prelink info data
571 if ( builder
->prelinkInfoExtraData
!= nullptr ) {
572 cacheBuilder
.setExtraPrelinkInfo(builder
->prelinkInfoExtraData
);
575 cacheBuilder
.buildAppCache(builder
->inputFiles
);
576 if ( !cacheBuilder
.errorMessage().empty() ) {
577 builder
->error("%s", cacheBuilder
.errorMessage().c_str());
578 generatePerKextErrors(builder
);
582 uint8_t* cacheBuffer
= nullptr;
583 uint64_t cacheSize
= 0;
584 cacheBuilder
.writeBuffer(cacheBuffer
, cacheSize
);
586 CFDataRef dataRef
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, cacheBuffer
, cacheSize
, kCFAllocatorDefault
);
587 builder
->retain(dataRef
);
590 CFArrayRef warningsArrayRef
= CFArrayCreate(kCFAllocatorDefault
, nullptr, 0, nullptr);
591 builder
->retain(warningsArrayRef
);
592 CFRelease(warningsArrayRef
);
594 CollectionFileResult_v1 fileResult
= { 1, kernelCollection
, dataRef
, warningsArrayRef
};
596 builder
->fileResultsStorage
.push_back(fileResult
);
597 builder
->fileResults
.push_back(&builder
->fileResultsStorage
.back());
602 // Gets the list of errors we have produced. These may be from incorrect input values, or failures in the build itself
603 __API_AVAILABLE(macos(10.14))
604 const char* const* getErrors(const struct KernelCollectionBuilder
* builder
, uint64_t* errorCount
) {
605 if (builder
->errors
.empty())
607 *errorCount
= builder
->errors
.size();
608 return builder
->errors
.data();
611 __API_AVAILABLE(macos(10.14))
612 CFDictionaryRef
getKextErrors(const struct KernelCollectionBuilder
* builder
) {
613 return builder
->errorsDict
;
616 // Returns an array of the resulting files. These may be new collections, or other files required later
617 __API_AVAILABLE(macos(10.14))
618 const struct CollectionFileResult_v1
* const* getCollectionFileResults(struct KernelCollectionBuilder
* builder
, uint64_t* resultCount
) {
619 if ( builder
->fileResults
.empty() ) {
624 *resultCount
= builder
->fileResults
.size();
625 return builder
->fileResults
.data();
628 __API_AVAILABLE(macos(10.14))
629 void destroyKernelCollectionBuilder(struct KernelCollectionBuilder
* builder
) {
630 for (CFTypeRef ref
: builder
->typeRefs
)
632 dyld3::closure::FileSystemNull fileSystem
;
633 for (const AppCacheBuilder::InputDylib
& inputFile
: builder
->inputFiles
) {
634 fileSystem
.unloadFile(inputFile
.dylib
.loadedFileInfo
);
636 if ( builder
->kernelCollectionFileInfo
.fileContent
!= nullptr) {
637 fileSystem
.unloadFile(builder
->kernelCollectionFileInfo
);
639 if ( builder
->pageableCollectionFileInfo
.fileContent
!= nullptr) {
640 fileSystem
.unloadFile(builder
->pageableCollectionFileInfo
);