]> git.saurik.com Git - apple/dyld.git/blob - dyld3/shared-cache/kernel_collection_builder.cpp
dyld-851.27.tar.gz
[apple/dyld.git] / dyld3 / shared-cache / kernel_collection_builder.cpp
1 /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
2 *
3 * Copyright (c) 2017 Apple Inc. All rights reserved.
4 *
5 * @APPLE_LICENSE_HEADER_START@
6 *
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
12 * file.
13 *
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.
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24
25 #include "kernel_collection_builder.h"
26
27 #include "AppCacheBuilder.h"
28 #include "Diagnostics.h"
29 #include "ClosureFileSystemNull.h"
30 #include "MachOAppCache.h"
31
32 #include <span>
33 #include <string>
34 #include <vector>
35
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
38
39 static const uint32_t MajorVersion = 1;
40 static const uint32_t MinorVersion = 0;
41
42 struct KernelCollectionBuilder {
43
44 KernelCollectionBuilder(const BuildOptions_v1* options);
45
46 struct CollectionFile {
47 const uint8_t* data = nullptr;
48 uint64_t size = 0;
49 const char* path = nullptr;
50 };
51
52 struct CacheBuffer {
53 uint8_t* buffer = nullptr;
54 uint64_t bufferSize = 0;
55 };
56
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;
65
66 std::list<CollectionFileResult_v1> fileResultsStorage;
67 std::vector<const CollectionFileResult_v1*> fileResults;
68 CFDictionaryRef errorsDict = nullptr;
69
70 std::vector<const char*> errors;
71 std::vector<std::string> errorStorage;
72
73 std::list<std::string> duplicatedStrings;
74 std::vector<CFTypeRef> typeRefs;
75
76 __attribute__((format(printf, 2, 3)))
77 void error(const char* format, ...) {
78 va_list list;
79 va_start(list, format);
80 Diagnostics diag;
81 diag.error(format, list);
82 va_end(list);
83
84 errorStorage.push_back(diag.errorMessage());
85 errors.push_back(errorStorage.back().data());
86 }
87
88 void retain(CFTypeRef v) {
89 CFRetain(v);
90 typeRefs.push_back(v);
91 }
92
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");
99 return nullptr;
100 }
101 duplicatedStrings.push_back(buffer);
102 return duplicatedStrings.back().c_str();
103 }
104
105 };
106
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;
111 }
112
113 KernelCollectionBuilder::KernelCollectionBuilder(const BuildOptions_v1* options)
114 : options(*options) {
115 retain(this->options.arch);
116 }
117
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);
122
123 if (options->version < kMinBuildVersion) {
124 builder->error("Builder version %llu is less than minimum supported version of %llu", options->version, kMinBuildVersion);
125 return builder;
126 }
127 if (options->version > kMaxBuildVersion) {
128 builder->error("Builder version %llu is greater than maximum supported version of %llu", options->version, kMaxBuildVersion);
129 return builder;
130 }
131 if ( options->arch == nullptr ) {
132 builder->error("arch must not be null");
133 return builder;
134 }
135 const char* archName = builder->strdup(options->arch);
136 if ( archName == nullptr ) {
137 // Already generated an error in strdup.
138 return builder;
139 }
140 builder->arch = archName;
141
142 return builder;
143 }
144
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;
153 fileInfo.inode = 0;
154 fileInfo.mtime = 0;
155 fileInfo.unload = nullptr;
156 fileInfo.path = builder->strdup(path);
157
158 Diagnostics diag;
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);
163 if ( !loaded ) {
164 builder->error("%s", diag.errorMessage().c_str());
165 return false;
166 }
167
168 return true;
169 }
170
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);
176
177 dyld3::closure::LoadedFileInfo info;
178 bool loaded = loadFileFromData(builder, path, data, info);
179 if ( !loaded )
180 return false;
181
182 DyldSharedCache::MappedMachO mappedFile(info.path, (const dyld3::MachOAnalyzer *)info.fileContent,
183 info.fileContentLen, false, false,
184 info.sliceOffset, info.mtime, info.inode);
185
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();
192
193 builder->inputFiles.push_back(input);
194 return true;
195 }
196
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);
202 return false;
203 }
204
205 builder->retain(fileData->dependencies);
206 builder->retain(fileData->bundleID);
207 builder->retain(fileData->bundlePath);
208 builder->retain(fileData->plist);
209
210 dyld3::closure::LoadedFileInfo info;
211
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);
218 if ( !loaded )
219 return false;
220 kextpath = info.path;
221 } else {
222 kextpath = "codeless";
223 }
224
225 DyldSharedCache::MappedMachO mappedFile(kextpath, (const dyld3::MachOAnalyzer *)info.fileContent,
226 info.fileContentLen, false, false,
227 info.sliceOffset, info.mtime, info.inode);
228
229 uint64_t numDependencies = CFArrayGetCount(fileData->dependencies);
230
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);
240 return false;
241 }
242 CFStringRef stringRef = (CFStringRef)elementRef;
243 input.dylibDeps.push_back(builder->strdup(stringRef));
244 }
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;
251 break;
252 case binaryStripNone:
253 input.stripMode = CacheBuilder::DylibStripMode::stripNone;
254 break;
255 case binaryStripExports:
256 input.stripMode = CacheBuilder::DylibStripMode::stripExports;
257 break;
258 case binaryStripLocals:
259 input.stripMode = CacheBuilder::DylibStripMode::stripLocals;
260 break;
261 case binaryStripAll:
262 input.stripMode = CacheBuilder::DylibStripMode::stripAll;
263 break;
264 }
265
266 builder->inputFiles.push_back(input);
267 return true;
268 }
269
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);
275
276 assert(0);
277 }
278
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");
287 return false;
288 }
289 fileInfo = &builder->kernelCollectionFileInfo;
290 } else if ( kind == pageableKC ) {
291 if ( builder->options.collectionKind != auxKC ) {
292 builder->error("Invalid collection file for build");
293 return false;
294 }
295 if ( builder->pageableCollectionFileInfo.fileContent != nullptr ) {
296 builder->error("Already have collection file");
297 return false;
298 }
299 fileInfo = &builder->pageableCollectionFileInfo;
300 } else {
301 builder->error("Unsupported collection kind");
302 return false;
303 }
304 if ( fileInfo->fileContent != nullptr ) {
305 builder->error("Already have collection file");
306 return false;
307 }
308
309 builder->retain(path);
310 builder->retain(data);
311
312 bool loaded = loadFileFromData(builder, path, data, *fileInfo);
313 if ( !loaded )
314 return false;
315
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));
319 return false;
320 }
321
322 return true;
323 }
324
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");
332 return false;
333 }
334 CFIndex segmentNameLength = CFStringGetLength(segmentName);
335 if ( (segmentNameLength == 0) || (segmentNameLength > 16) ) {
336 builder->error("Segment data name must not be empty or > 16 characters");
337 return false;
338 }
339
340 AppCacheBuilder::CustomSegment::CustomSection section;
341
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");
348 return false;
349 }
350 section.sectionName = builder->strdup(sectionName);
351 }
352 }
353
354 // Check the data
355 if ( data == nullptr ) {
356 builder->error("Segment data payload must be non-null");
357 return false;
358 }
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");
363 return false;
364 }
365
366 builder->retain(data);
367 section.data.insert(section.data.end(), dataStart, dataStart + dataLength);
368
369 AppCacheBuilder::CustomSegment segment;
370 segment.segmentName = builder->strdup(segmentName);
371 segment.sections.push_back(section);
372 builder->customSections.push_back(segment);
373 return true;
374 }
375
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");
381 return false;
382 }
383 // Check the data
384 if ( extraData == nullptr ) {
385 builder->error("Prelink info data payload must be non-null");
386 return false;
387 }
388 if ( CFDictionaryGetCount(extraData) == 0 ) {
389 builder->error("Prelink info data payload must not be empty");
390 return false;
391 }
392 builder->retain(extraData);
393 builder->prelinkInfoExtraData = extraData;
394 return true;
395 }
396
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) {
400 assert(0);
401 }
402
403 static AppCacheBuilder::Options::AppCacheKind cacheKind(CollectionKind kind) {
404 switch (kind) {
405 case unknownKC:
406 return AppCacheBuilder::Options::AppCacheKind::none;
407 case baseKC:
408 return AppCacheBuilder::Options::AppCacheKind::kernel;
409 case auxKC:
410 return AppCacheBuilder::Options::AppCacheKind::auxKC;
411 case pageableKC:
412 return AppCacheBuilder::Options::AppCacheKind::pageableKC;
413 }
414 }
415
416 static AppCacheBuilder::Options::StripMode stripMode(StripMode mode) {
417 switch (mode) {
418 case unknownStripMode:
419 return AppCacheBuilder::Options::StripMode::none;
420 case stripNone:
421 return AppCacheBuilder::Options::StripMode::none;
422 case stripAll:
423 return AppCacheBuilder::Options::StripMode::all;
424 case stripAllKexts:
425 return AppCacheBuilder::Options::StripMode::allExceptKernel;
426 }
427 }
428
429 static void generatePerKextErrors(struct KernelCollectionBuilder* builder) {
430 CFMutableDictionaryRef errorsDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, nullptr, nullptr);
431 builder->typeRefs.push_back(errorsDict);
432
433 for (const AppCacheBuilder::InputDylib& file : builder->inputFiles) {
434 if ( !file.errors->hasError() )
435 continue;
436
437 CFStringRef bundleID = CFStringCreateWithCString(kCFAllocatorDefault, file.dylibID.c_str(),
438 kCFStringEncodingASCII);
439 builder->typeRefs.push_back(bundleID);
440
441 CFMutableArrayRef errorsArray = CFArrayCreateMutable(kCFAllocatorDefault, 1, nullptr);
442 builder->typeRefs.push_back(errorsArray);
443
444 CFStringRef errorString = CFStringCreateWithCString(kCFAllocatorDefault, file.errors->errorMessage().c_str(),
445 kCFStringEncodingASCII);
446 builder->typeRefs.push_back(errorString);
447
448 CFArrayAppendValue(errorsArray, errorString);
449
450 // Add this bundle to the dictionary
451 CFDictionarySetValue(errorsDict, bundleID, errorsArray);
452
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());
456 }
457
458 if ( CFDictionaryGetCount(errorsDict) != 0 ) {
459 builder->errorsDict = errorsDict;
460 }
461 }
462
463 // Returns true on success.
464 __API_AVAILABLE(macos(10.14))
465 bool runKernelCollectionBuilder(struct KernelCollectionBuilder* builder) {
466
467 // Make sure specificed bundle-id's are not already in the caches we
468 // are linking to
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");
473 return false;
474 }
475 Diagnostics diag;
476
477 // Check the base KC
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);
481 });
482
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);
488 });
489 }
490
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;
496 }
497 }
498 if ( foundBadBundle )
499 return false;
500 }
501
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);
506 };
507 const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)input.dylib.loadedFileInfo.fileContent;
508 // Skip codeless kexts
509 if ( ma == nullptr )
510 return;
511 if (!ma->canBePlacedInKernelCollection(input.dylib.loadedFileInfo.path, errorHandler)) {
512 assert(input.errors->hasError());
513 }
514 });
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);
519 return false;
520 }
521 }
522
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 = {};
545
546 AppCacheBuilder::Options appCacheOptions;
547 appCacheOptions.cacheKind = cacheKind(builder->options.collectionKind);
548 appCacheOptions.stripMode = stripMode(builder->options.stripMode);
549
550 const dyld3::closure::FileSystemNull builderFileSystem;
551
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);
556 }
557 if ( builder->pageableCollectionFileInfo.fileContent != nullptr ) {
558 const dyld3::MachOAppCache* appCacheMA = (const dyld3::MachOAppCache*)builder->pageableCollectionFileInfo.fileContent;
559 cacheBuilder.setExistingPageableKernelCollection(appCacheMA);
560 }
561
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());
566 return false;
567 }
568 }
569
570 // Add prelink info data
571 if ( builder->prelinkInfoExtraData != nullptr ) {
572 cacheBuilder.setExtraPrelinkInfo(builder->prelinkInfoExtraData);
573 }
574
575 cacheBuilder.buildAppCache(builder->inputFiles);
576 if ( !cacheBuilder.errorMessage().empty() ) {
577 builder->error("%s", cacheBuilder.errorMessage().c_str());
578 generatePerKextErrors(builder);
579 return false;
580 }
581
582 uint8_t* cacheBuffer = nullptr;
583 uint64_t cacheSize = 0;
584 cacheBuilder.writeBuffer(cacheBuffer, cacheSize);
585
586 CFDataRef dataRef = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, cacheBuffer, cacheSize, kCFAllocatorDefault);
587 builder->retain(dataRef);
588 CFRelease(dataRef);
589
590 CFArrayRef warningsArrayRef = CFArrayCreate(kCFAllocatorDefault, nullptr, 0, nullptr);
591 builder->retain(warningsArrayRef);
592 CFRelease(warningsArrayRef);
593
594 CollectionFileResult_v1 fileResult = { 1, kernelCollection, dataRef, warningsArrayRef };
595
596 builder->fileResultsStorage.push_back(fileResult);
597 builder->fileResults.push_back(&builder->fileResultsStorage.back());
598
599 return true;
600 }
601
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())
606 return nullptr;
607 *errorCount = builder->errors.size();
608 return builder->errors.data();
609 }
610
611 __API_AVAILABLE(macos(10.14))
612 CFDictionaryRef getKextErrors(const struct KernelCollectionBuilder* builder) {
613 return builder->errorsDict;
614 }
615
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() ) {
620 *resultCount = 0;
621 return nullptr;
622 }
623
624 *resultCount = builder->fileResults.size();
625 return builder->fileResults.data();
626 }
627
628 __API_AVAILABLE(macos(10.14))
629 void destroyKernelCollectionBuilder(struct KernelCollectionBuilder* builder) {
630 for (CFTypeRef ref : builder->typeRefs)
631 CFRelease(ref);
632 dyld3::closure::FileSystemNull fileSystem;
633 for (const AppCacheBuilder::InputDylib& inputFile : builder->inputFiles) {
634 fileSystem.unloadFile(inputFile.dylib.loadedFileInfo);
635 }
636 if ( builder->kernelCollectionFileInfo.fileContent != nullptr) {
637 fileSystem.unloadFile(builder->kernelCollectionFileInfo);
638 }
639 if ( builder->pageableCollectionFileInfo.fileContent != nullptr) {
640 fileSystem.unloadFile(builder->pageableCollectionFileInfo);
641 }
642 delete builder;
643 }