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 "mrm_shared_cache_builder.h"
26 #include "SharedCacheBuilder.h"
27 #include "ClosureFileSystem.h"
28 #include "FileUtils.h"
29 #include "JSONReader.h"
36 #if __has_include(<Cambria/SharedCache.h>)
37 #include <Cambria/SharedCache.h>
38 asm(".linker_option \"-lcambria_sharedcache\"");
39 asm(".linker_option \"-lcodedirectory_static\"");
42 static const uint64_t kMinBuildVersion
= 1; //The minimum version BuildOptions struct we can support
43 static const uint64_t kMaxBuildVersion
= 2; //The maximum version BuildOptions struct we can support
45 static const uint32_t MajorVersion
= 1;
46 static const uint32_t MinorVersion
= 2;
54 const uint64_t length
;
60 class FileSystemMRM
: public FileSystem
{
62 FileSystemMRM() : FileSystem() { }
64 bool getRealPath(const char possiblePath
[MAXPATHLEN
], char realPath
[MAXPATHLEN
]) const override
{
66 std::string resolvedPath
= symlinkResolver
.realPath(diag
, possiblePath
);
67 if (diag
.hasError()) {
68 diag
.verbose("MRM error: %s\n", diag
.errorMessage().c_str());
73 // FIXME: Should we only return real paths of files which point to macho's? For now that is what we are doing
74 auto it
= fileMap
.find(resolvedPath
);
75 if (it
== fileMap
.end())
78 memcpy(realPath
, resolvedPath
.c_str(), std::min((size_t)MAXPATHLEN
, resolvedPath
.size() + 1));
82 bool loadFile(const char* path
, LoadedFileInfo
& info
, char realerPath
[MAXPATHLEN
], void (^error
)(const char* format
, ...)) const override
{
84 std::string resolvedPath
= symlinkResolver
.realPath(diag
, path
);
85 if (diag
.hasError()) {
86 diag
.verbose("MRM error: %s\n", diag
.errorMessage().c_str());
91 auto it
= fileMap
.find(resolvedPath
);
92 if (it
== fileMap
.end())
95 if (resolvedPath
== path
)
98 memcpy(realerPath
, resolvedPath
.c_str(), std::min((size_t)MAXPATHLEN
, resolvedPath
.size() + 1));
100 // The file exists at this exact path. Lets use it!
101 const FileInfo
& fileInfo
= files
[it
->second
];
103 info
.fileContent
= fileInfo
.data
;
104 info
.fileContentLen
= fileInfo
.length
;
105 info
.sliceOffset
= 0;
106 info
.sliceLen
= fileInfo
.length
;
107 info
.isOSBinary
= true;
108 info
.inode
= fileInfo
.inode
;
109 info
.mtime
= fileInfo
.mtime
;
110 info
.unload
= nullptr;
115 void unloadFile(const LoadedFileInfo
& info
) const override
{
120 void unloadPartialFile(LoadedFileInfo
& info
, uint64_t keepStartOffset
, uint64_t keepLength
) const override
{
121 // Note we don't actually unload the data here, but we do want to update the offsets for other data structures to track where we are
122 info
.fileContent
= (const void*)((char*)info
.fileContent
+ keepStartOffset
);
123 info
.fileContentLen
= keepLength
;
126 bool fileExists(const char* path
, uint64_t* inode
=nullptr, uint64_t* mtime
=nullptr,
127 bool* issetuid
=nullptr, bool* inodesMatchRuntime
= nullptr) const override
{
129 std::string resolvedPath
= symlinkResolver
.realPath(diag
, path
);
130 if (diag
.hasError()) {
131 diag
.verbose("MRM error: %s\n", diag
.errorMessage().c_str());
136 auto it
= fileMap
.find(resolvedPath
);
137 if (it
== fileMap
.end())
140 // The file exists at this exact path. Lets use it!
141 const FileInfo
& fileInfo
= files
[it
->second
];
143 *inode
= fileInfo
.inode
;
145 *mtime
= fileInfo
.mtime
;
148 if (inodesMatchRuntime
)
149 *inodesMatchRuntime
= false;
154 bool addFile(const char* path
, uint8_t* data
, uint64_t size
, Diagnostics
& diag
, FileFlags fileFlags
) {
155 auto iteratorAndInserted
= fileMap
.insert(std::make_pair(path
, files
.size()));
156 if (!iteratorAndInserted
.second
) {
157 diag
.error("Already have content for path: '%s'", path
);
161 symlinkResolver
.addFile(diag
, path
);
165 // on iOS, inode is just a placeholder
166 // Note its safe to just use the index here as we only compare it during closure building
167 // and never record it in the closures
168 uint64_t inode
= files
.size() + 1;
171 files
.push_back((FileInfo
){ path
, data
, size
, fileFlags
, mtime
, inode
});
175 bool addSymlink(const char* fromPath
, const char* toPath
, Diagnostics
& diag
) {
176 symlinkResolver
.addSymlink(diag
, fromPath
, toPath
);
177 return !diag
.hasError();
180 void forEachFileInfo(std::function
<void(const char* path
, FileFlags fileFlags
)> lambda
) {
181 for (const FileInfo
& fileInfo
: files
)
182 lambda(fileInfo
.path
, fileInfo
.flags
);
185 size_t fileCount() const {
189 std::vector
<DyldSharedCache::FileAlias
> getResolvedSymlinks(Diagnostics
& diag
) {
190 return symlinkResolver
.getResolvedSymlinks(diag
);
194 std::vector
<FileInfo
> files
;
195 std::map
<std::string
, uint64_t> fileMap
;
196 SymlinkResolver symlinkResolver
;
199 } // namespace closure
202 struct BuildInstance
{
203 std::unique_ptr
<DyldSharedCache::CreateOptions
> options
;
204 std::unique_ptr
<SharedCacheBuilder
> builder
;
205 std::vector
<CacheBuilder::InputFile
> inputFiles
;
206 std::vector
<const char*> errors
;
207 std::vector
<const char*> warnings
;
208 std::vector
<std::string
> errorStrings
; // Owns the data for the errors
209 std::vector
<std::string
> warningStrings
; // Owns the data for the warnings
210 uint8_t* cacheData
= nullptr;
211 uint64_t cacheSize
= 0;
213 std::string macOSMap
; // For compatibility with update_dyld_shared_cache's .map file
214 std::string macOSMapPath
; // Owns the string for the path
215 std::string cdHash
; // Owns the data for the cdHash
216 std::string cdHashType
; // Owns the data for the cdHashType
217 std::string uuid
; // Owns the data for the uuid
220 struct BuildFileResult
{
226 struct TranslationResult
{
231 bool bufferWasMalloced
;
234 struct MRMSharedCacheBuilder
{
235 MRMSharedCacheBuilder(const BuildOptions_v1
* options
);
236 const BuildOptions_v1
* options
;
237 dyld3::closure::FileSystemMRM fileSystem
;
239 std::string dylibOrderFileData
;
240 std::string dirtyDataOrderFileData
;
241 void* objcOptimizationsFileData
;
242 size_t objcOptimizationsFileLength
;
244 // An array of builders and their options as we may have more than one builder for a given device variant.
245 std::vector
<BuildInstance
> builders
;
247 // The paths in all of the caches
248 // We keep this here to own the std::string path data
249 std::map
<std::string
, std::unordered_set
<const BuildInstance
*>> dylibsInCaches
;
251 // The results from all of the builders
252 // We keep this in a vector to own the data.
253 std::vector
<FileResult
*> fileResults
;
254 std::vector
<FileResult
> fileResultStorage
;
255 std::vector
<std::pair
<uint64_t, bool>> fileResultBuffers
;
257 // The results from all of the builders
258 // We keep this in a vector to own the data.
259 std::vector
<CacheResult
*> cacheResults
;
260 std::vector
<CacheResult
> cacheResultStorage
;
262 #if __has_include(<Cambria/SharedCache.h>)
263 // Storage for translated shared caches
264 std::vector
<TranslationResult
> translationResults
;
267 // The files to remove. These are in every copy of the caches we built
268 std::vector
<const char*> filesToRemove
;
270 std::vector
<const char*> errors
;
271 std::vector
<std::string
> errorStorage
;
272 pthread_mutex_t lock
;
280 State state
= AcceptingFiles
;
282 void runSync(void (^block
)()) {
283 pthread_mutex_lock(&lock
);
285 pthread_mutex_unlock(&lock
);
288 __attribute__((format(printf
, 2, 3)))
289 void error(const char* format
, ...) {
291 va_start(list
, format
);
293 diag
.error(format
, list
);
296 errorStorage
.push_back(diag
.errorMessage());
297 errors
.push_back(errorStorage
.back().data());
301 MRMSharedCacheBuilder::MRMSharedCacheBuilder(const BuildOptions_v1
* options
)
303 , lock(PTHREAD_MUTEX_INITIALIZER
)
304 , objcOptimizationsFileData(nullptr)
305 , objcOptimizationsFileLength(0)
310 void validiateBuildOptions(const BuildOptions_v1
* options
, MRMSharedCacheBuilder
& builder
) {
311 if (options
->version
< kMinBuildVersion
) {
312 builder
.error("Builder version %llu is less than minimum supported version of %llu", options
->version
, kMinBuildVersion
);
314 if (options
->version
> kMaxBuildVersion
) {
315 builder
.error("Builder version %llu is greater than maximum supported version of %llu", options
->version
, kMaxBuildVersion
);
317 if (!options
->updateName
) {
318 builder
.error("updateName must not be null");
320 if (!options
->deviceName
) {
321 builder
.error("deviceName must not be null");
323 switch (options
->disposition
) {
324 case Disposition::Unknown
:
325 case Disposition::InternalDevelopment
:
326 case Disposition::Customer
:
327 case Disposition::InternalMinDevelopment
:
330 builder
.error("unknown disposition value");
333 switch (options
->platform
) {
334 case Platform::unknown
:
335 builder
.error("platform must not be unknown");
337 case Platform::macOS
:
340 case Platform::watchOS
:
341 case Platform::bridgeOS
:
342 case Platform::iOSMac
:
343 case Platform::iOS_simulator
:
344 case Platform::tvOS_simulator
:
345 case Platform::watchOS_simulator
:
348 builder
.error("unknown platform value");
351 if (!options
->archs
) {
352 builder
.error("archs must not be null");
354 if (!options
->numArchs
) {
355 builder
.error("numArchs must not be 0");
359 void getVersion(uint32_t *major
, uint32_t *minor
) {
360 *major
= MajorVersion
;
361 *minor
= MinorVersion
;
364 struct MRMSharedCacheBuilder
* createSharedCacheBuilder(const BuildOptions_v1
* options
) {
365 MRMSharedCacheBuilder
* builder
= new MRMSharedCacheBuilder(options
);
367 // Check the option struct values are valid
368 validiateBuildOptions(options
, *builder
);
373 bool addFile(struct MRMSharedCacheBuilder
* builder
, const char* path
, uint8_t* data
, uint64_t size
, FileFlags fileFlags
) {
374 __block
bool success
= false;
375 builder
->runSync(^() {
376 if (builder
->state
!= MRMSharedCacheBuilder::AcceptingFiles
) {
377 builder
->error("Cannot add file: '%s' as we have already started building", path
);
380 size_t pathLength
= strlen(path
);
381 if (pathLength
== 0) {
382 builder
->error("Empty path");
385 if (pathLength
>= MAXPATHLEN
) {
386 builder
->error("Path is too long: '%s'", path
);
389 if (data
== nullptr) {
390 builder
->error("Data cannot be null for file: '%s'", path
);
396 case ShouldBeExcludedFromCacheIfUnusedLeaf
:
397 case RequiredClosure
:
400 builder
->dylibOrderFileData
= std::string((char*)data
, size
);
403 case DirtyDataOrderFile
:
404 builder
->dirtyDataOrderFileData
= std::string((char*)data
, size
);
407 case ObjCOptimizationsFile
:
408 builder
->objcOptimizationsFileData
= data
;
409 builder
->objcOptimizationsFileLength
= size
;
413 builder
->error("unknown file flags value");
417 if (!builder
->fileSystem
.addFile(path
, data
, size
, diag
, fileFlags
)) {
418 builder
->errorStorage
.push_back(diag
.errorMessage());
419 builder
->errors
.push_back(builder
->errorStorage
.back().data());
427 bool addSymlink(struct MRMSharedCacheBuilder
* builder
, const char* fromPath
, const char* toPath
) {
428 __block
bool success
= false;
429 builder
->runSync(^() {
430 if (builder
->state
!= MRMSharedCacheBuilder::AcceptingFiles
) {
431 builder
->error("Cannot add file: '%s' as we have already started building", fromPath
);
434 size_t pathLength
= strlen(fromPath
);
435 if (pathLength
== 0) {
436 builder
->error("Empty path");
439 if (pathLength
>= MAXPATHLEN
) {
440 builder
->error("Path is too long: '%s'", fromPath
);
444 if (!builder
->fileSystem
.addSymlink(fromPath
, toPath
, diag
)) {
445 builder
->errorStorage
.push_back(diag
.errorMessage());
446 builder
->errors
.push_back(builder
->errorStorage
.back().data());
454 static DyldSharedCache::LocalSymbolsMode
platformExcludeLocalSymbols(Platform platform
) {
456 case Platform::unknown
:
457 case Platform::macOS
:
458 return DyldSharedCache::LocalSymbolsMode::keep
;
461 case Platform::watchOS
:
462 case Platform::bridgeOS
:
463 return DyldSharedCache::LocalSymbolsMode::unmap
;
464 case Platform::iOSMac
:
465 case Platform::iOS_simulator
:
466 case Platform::tvOS_simulator
:
467 case Platform::watchOS_simulator
:
468 return DyldSharedCache::LocalSymbolsMode::keep
;
472 static DyldSharedCache::LocalSymbolsMode
excludeLocalSymbols(const BuildOptions_v1
* options
) {
473 if ( options
->version
>= 2 ) {
474 const BuildOptions_v2
* v2
= (const BuildOptions_v2
*)options
;
475 if ( v2
->optimizeForSize
)
476 return DyldSharedCache::LocalSymbolsMode::strip
;
479 // Old build options always use the platform default
480 return platformExcludeLocalSymbols(options
->platform
);
483 static bool optimizeDyldDlopens(const BuildOptions_v1
* options
) {
484 // Old builds always default to dyld3 optimisations
485 if ( options
->version
< 2 ) {
489 // If we want to optimize for size instead of speed, then disable dyld3 dlopen closures
490 const BuildOptions_v2
* v2
= (const BuildOptions_v2
*)options
;
491 return !v2
->optimizeForSize
;
494 static DyldSharedCache::CodeSigningDigestMode
platformCodeSigningDigestMode(Platform platform
) {
496 case Platform::unknown
:
497 case Platform::macOS
:
500 return DyldSharedCache::SHA256only
;
501 case Platform::watchOS
:
502 return DyldSharedCache::Agile
;
503 case Platform::bridgeOS
:
504 case Platform::iOSMac
:
505 case Platform::iOS_simulator
:
506 case Platform::tvOS_simulator
:
507 case Platform::watchOS_simulator
:
508 return DyldSharedCache::SHA256only
;
512 static bool platformIsForSimulator(Platform platform
) {
514 case Platform::unknown
:
515 case Platform::macOS
:
518 case Platform::watchOS
:
519 case Platform::bridgeOS
:
520 case Platform::iOSMac
:
522 case Platform::iOS_simulator
:
523 case Platform::tvOS_simulator
:
524 case Platform::watchOS_simulator
:
529 static const char* dispositionName(Disposition disposition
) {
530 switch (disposition
) {
531 case Disposition::Unknown
:
533 case Disposition::InternalDevelopment
:
535 case Disposition::Customer
:
537 case Disposition::InternalMinDevelopment
:
538 return "InternalMinDevelopment";
542 // This is a JSON file containing the list of classes for which
543 // we should try to build IMP caches.
544 dyld3::json::Node
parseObjcOptimizationsFile(Diagnostics
& diags
, const void* data
, size_t length
) {
545 return dyld3::json::readJSON(diags
, data
, length
);
548 bool runSharedCacheBuilder(struct MRMSharedCacheBuilder
* builder
) {
549 __block
bool success
= false;
550 builder
->runSync(^() {
551 if (builder
->state
!= MRMSharedCacheBuilder::AcceptingFiles
) {
552 builder
->error("Builder has already been run");
555 builder
->state
= MRMSharedCacheBuilder::Building
;
556 if (builder
->fileSystem
.fileCount() == 0) {
557 builder
->error("Cannot run builder with no files");
560 __block Diagnostics diag
;
561 std::vector
<DyldSharedCache::FileAlias
> aliases
= builder
->fileSystem
.getResolvedSymlinks(diag
);
562 if (diag
.hasError()) {
563 diag
.verbose("Symlink resolver error: %s\n", diag
.errorMessage().c_str());
566 if (!builder
->errors
.empty()) {
567 builder
->error("Skipping running shared cache builder due to previous errors");
571 __block
std::vector
<SharedCacheBuilder::InputFile
> inputFiles
;
572 builder
->fileSystem
.forEachFileInfo(^(const char* path
, FileFlags fileFlags
) {
573 SharedCacheBuilder::InputFile::State state
= SharedCacheBuilder::InputFile::Unset
;
575 case FileFlags::NoFlags
:
576 state
= SharedCacheBuilder::InputFile::Unset
;
578 case FileFlags::MustBeInCache
:
579 state
= SharedCacheBuilder::InputFile::MustBeIncluded
;
581 case FileFlags::ShouldBeExcludedFromCacheIfUnusedLeaf
:
582 state
= SharedCacheBuilder::InputFile::MustBeExcludedIfUnused
;
584 case FileFlags::RequiredClosure
:
585 state
= SharedCacheBuilder::InputFile::MustBeIncluded
;
587 case FileFlags::DylibOrderFile
:
588 case FileFlags::DirtyDataOrderFile
:
589 case FileFlags::ObjCOptimizationsFile
:
590 builder
->error("Order files should not be in the file system");
593 inputFiles
.emplace_back((SharedCacheBuilder::InputFile
){ path
, state
});
596 auto addCacheConfiguration
= ^(bool isOptimized
) {
597 for (uint64_t i
= 0; i
!= builder
->options
->numArchs
; ++i
) {
598 // HACK: Skip i386 for macOS
599 if ( (builder
->options
->platform
== Platform::macOS
) && (strcmp(builder
->options
->archs
[i
], "i386") == 0 ) )
601 auto options
= std::make_unique
<DyldSharedCache::CreateOptions
>((DyldSharedCache::CreateOptions
){});
602 const char *cacheSuffix
= (isOptimized
? "" : ".development");
603 if ( builder
->options
->platform
== Platform::macOS
)
605 std::string runtimePath
= (builder
->options
->platform
== Platform::macOS
) ? MACOSX_MRM_DYLD_SHARED_CACHE_DIR
: IPHONE_DYLD_SHARED_CACHE_DIR
;
606 options
->outputFilePath
= runtimePath
+ "dyld_shared_cache_" + builder
->options
->archs
[i
] + cacheSuffix
;
607 options
->outputMapFilePath
= options
->outputFilePath
+ ".json";
608 options
->archs
= &dyld3::GradedArchs::forName(builder
->options
->archs
[i
]);
609 options
->platform
= (dyld3::Platform
)builder
->options
->platform
;
610 options
->localSymbolMode
= excludeLocalSymbols(builder
->options
);
611 options
->optimizeStubs
= isOptimized
;
612 options
->optimizeDyldDlopens
= optimizeDyldDlopens(builder
->options
);
613 options
->optimizeDyldLaunches
= true;
614 options
->codeSigningDigestMode
= platformCodeSigningDigestMode(builder
->options
->platform
);
615 options
->dylibsRemovedDuringMastering
= true;
616 options
->inodesAreSameAsRuntime
= false;
617 options
->cacheSupportsASLR
= true;
618 options
->forSimulator
= platformIsForSimulator(builder
->options
->platform
);
619 options
->isLocallyBuiltCache
= builder
->options
->isLocallyBuiltCache
;
620 options
->verbose
= builder
->options
->verboseDiagnostics
;
621 options
->evictLeafDylibsOnOverflow
= true;
622 options
->loggingPrefix
= std::string(builder
->options
->deviceName
) + dispositionName(builder
->options
->disposition
) + "." + builder
->options
->archs
[i
] + cacheSuffix
;
623 options
->dylibOrdering
= parseOrderFile(builder
->dylibOrderFileData
);
624 options
->dirtyDataSegmentOrdering
= parseOrderFile(builder
->dirtyDataOrderFileData
);
625 options
->objcOptimizations
= parseObjcOptimizationsFile(diag
, builder
->objcOptimizationsFileData
, builder
->objcOptimizationsFileLength
);
627 auto cacheBuilder
= std::make_unique
<SharedCacheBuilder
>(*options
.get(), builder
->fileSystem
);
628 builder
->builders
.emplace_back((BuildInstance
) { std::move(options
), std::move(cacheBuilder
), inputFiles
});
632 // Enqueue a cache for each configuration
633 switch (builder
->options
->disposition
) {
634 case Disposition::Unknown
:
635 case Disposition::InternalDevelopment
:
636 // HACK: MRM for the mac should only get development, even if it requested both
637 if (builder
->options
->platform
== Platform::macOS
) {
638 addCacheConfiguration(false);
640 addCacheConfiguration(false);
641 addCacheConfiguration(true);
644 case Disposition::Customer
:
645 addCacheConfiguration(true);
647 case Disposition::InternalMinDevelopment
:
648 addCacheConfiguration(false);
652 // FIXME: This step can run in parallel.
653 for (auto& buildInstance
: builder
->builders
) {
654 SharedCacheBuilder
* cacheBuilder
= buildInstance
.builder
.get();
655 cacheBuilder
->build(buildInstance
.inputFiles
, aliases
);
657 // First put the warnings in to a vector to own them.
658 buildInstance
.warningStrings
.reserve(cacheBuilder
->warnings().size());
659 for (const std::string
& warning
: cacheBuilder
->warnings())
660 buildInstance
.warningStrings
.push_back(warning
);
662 // Then copy to a vector to reference the owner
663 buildInstance
.warnings
.reserve(buildInstance
.warningStrings
.size());
664 for (const std::string
& warning
: buildInstance
.warningStrings
)
665 buildInstance
.warnings
.push_back(warning
.c_str());
667 if (!cacheBuilder
->errorMessage().empty()) {
668 // First put the errors in to a vector to own them.
669 buildInstance
.errorStrings
.push_back(cacheBuilder
->errorMessage());
671 // Then copy to a vector to reference the owner
672 buildInstance
.errors
.reserve(buildInstance
.errorStrings
.size());
673 for (const std::string
& error
: buildInstance
.errorStrings
)
674 buildInstance
.errors
.push_back(error
.c_str());
677 if (cacheBuilder
->errorMessage().empty()) {
678 cacheBuilder
->writeBuffer(buildInstance
.cacheData
, buildInstance
.cacheSize
);
679 buildInstance
.jsonMap
= cacheBuilder
->getMapFileJSONBuffer(builder
->options
->deviceName
);
680 if ( buildInstance
.options
->platform
== dyld3::Platform::macOS
) {
681 // For compatibility with update_dyld_shared_cache, put a .map file next to the shared cache
682 buildInstance
.macOSMap
= cacheBuilder
->getMapFileBuffer();
683 buildInstance
.macOSMapPath
= buildInstance
.options
->outputFilePath
+ ".map";
685 buildInstance
.cdHash
= cacheBuilder
->cdHashFirst();
686 buildInstance
.uuid
= cacheBuilder
->uuid();
687 switch (buildInstance
.options
->codeSigningDigestMode
) {
688 case DyldSharedCache::SHA256only
:
689 buildInstance
.cdHashType
= "sha256";
691 case DyldSharedCache::SHA1only
:
692 buildInstance
.cdHashType
= "sha1";
694 case DyldSharedCache::Agile
:
695 buildInstance
.cdHashType
= "sha1";
699 // Track the dylibs which were included in this cache
700 cacheBuilder
->forEachCacheDylib(^(const std::string
&path
) {
701 builder
->dylibsInCaches
[path
.c_str()].insert(&buildInstance
);
703 cacheBuilder
->forEachCacheSymlink(^(const std::string
&path
) {
704 builder
->dylibsInCaches
[path
.c_str()].insert(&buildInstance
);
707 // Free the cache builder now so that we don't keep too much memory resident
708 cacheBuilder
->deleteBuffer();
709 buildInstance
.builder
.reset();
712 #if __has_include(<Cambria/SharedCache.h>)
713 // Only build the cambria caches if we have x86_64(h) and at least one other arch
714 bool gotX86Cache
= false;
715 bool gotOtherCache
= false;
716 for (uint64_t i
= 0; i
!= builder
->options
->numArchs
; ++i
) {
717 const char* arch
= builder
->options
->archs
[i
];
718 if ( !strcmp(arch
, "x86_64") || !strcmp(arch
, "x86_64h") ) {
722 gotOtherCache
= true;
725 if ( (builder
->options
->platform
== Platform::macOS
) && gotX86Cache
&& gotOtherCache
) {
726 // Now create an Aot shared cache for any applicable caches
727 for (const auto& buildInstance
: builder
->builders
) {
728 // Only convert the x86_64 cache
729 if ( buildInstance
.options
->archs
!= &dyld3::GradedArchs::x86_64
)
732 // Skip failed caches
733 if (!buildInstance
.errors
.empty())
736 const uint8_t* aotCacheData
;
738 std::string cdHashStr
;
740 // Translate this shared cache file.
741 cambria::TranslationOptions options
;
743 #if !defined(RC_HIDE_J274) || !RC_HIDE_J274
744 // <rdar://problem/63767839> Don't generate a Tonga shared cache translation in the seed trains
745 cambria::translate_shared_cache(options
, buildInstance
.cacheData
, buildInstance
.cacheSize
,
746 &aotCacheData
, &aotCacheSize
, &cdHashStr
);
748 builder
->translationResults
.push_back({
749 .data
= aotCacheData
,
750 .size
= aotCacheSize
,
752 .path
= std::string(cambria::kAotSharedCachePath
),
753 .bufferWasMalloced
= false
757 // Now translate again for aruba based systems
758 options
.is_aruba_shared_cache
= true;
759 cambria::translate_shared_cache(options
, buildInstance
.cacheData
, buildInstance
.cacheSize
,
760 &aotCacheData
, &aotCacheSize
, &cdHashStr
);
762 bool bufferWasMalloced
= false;
763 #if !defined(RC_HIDE_J274) || !RC_HIDE_J274
764 // For GM we don't need to ship the Aruba AOT any more. It will be converted on the fly
765 // for the DTK. We'll just put a 0-byte placeholder instead and record the cdHash
766 vm_deallocate(mach_task_self(), (vm_address_t
)aotCacheData
, aotCacheSize
);
767 aotCacheData
= (uint8_t*)calloc(1, 1);
769 bufferWasMalloced
= true;
772 builder
->translationResults
.push_back({
773 .data
= aotCacheData
,
774 .size
= aotCacheSize
,
776 .path
= std::string(cambria::kAotSharedCachePathAruba
),
777 .bufferWasMalloced
= bufferWasMalloced
781 // Convert translation results into File results
782 for (auto& translationResult
: builder
->translationResults
) {
783 FileResult cacheFileResult
;
784 cacheFileResult
.version
= 1;
785 cacheFileResult
.behavior
= AddFile
;
786 cacheFileResult
.path
= translationResult
.path
.c_str();
787 cacheFileResult
.data
= translationResult
.data
;
788 cacheFileResult
.size
= translationResult
.size
;
789 cacheFileResult
.hashArch
= "x86_64";
790 cacheFileResult
.hashType
= "sha256";
791 cacheFileResult
.hash
= translationResult
.cdHash
.c_str();
793 builder
->fileResultBuffers
.push_back({ builder
->fileResultStorage
.size(), translationResult
.bufferWasMalloced
});
794 builder
->fileResultStorage
.emplace_back(cacheFileResult
);
799 // Now that we have run all of the builds, collect the results
800 // First push file results for each of the shared caches we built
801 for (auto& buildInstance
: builder
->builders
) {
802 CacheResult cacheBuildResult
;
803 cacheBuildResult
.version
= 1;
804 cacheBuildResult
.loggingPrefix
= buildInstance
.options
->loggingPrefix
.c_str();
805 cacheBuildResult
.deviceConfiguration
= buildInstance
.options
->loggingPrefix
.c_str();
806 cacheBuildResult
.warnings
= buildInstance
.warnings
.empty() ? nullptr : buildInstance
.warnings
.data();
807 cacheBuildResult
.numWarnings
= buildInstance
.warnings
.size();
808 cacheBuildResult
.errors
= buildInstance
.errors
.empty() ? nullptr : buildInstance
.errors
.data();
809 cacheBuildResult
.numErrors
= buildInstance
.errors
.size();
810 cacheBuildResult
.uuidString
= buildInstance
.uuid
.c_str();
811 cacheBuildResult
.mapJSON
= buildInstance
.jsonMap
.c_str();
813 builder
->cacheResultStorage
.emplace_back(cacheBuildResult
);
815 if (!buildInstance
.errors
.empty())
818 FileResult cacheFileResult
;
819 cacheFileResult
.version
= 1;
820 cacheFileResult
.path
= buildInstance
.options
->outputFilePath
.c_str();
821 cacheFileResult
.behavior
= AddFile
;
822 cacheFileResult
.data
= buildInstance
.cacheData
;
823 cacheFileResult
.size
= buildInstance
.cacheSize
;
824 cacheFileResult
.hashArch
= buildInstance
.options
->archs
->name();
825 cacheFileResult
.hashType
= buildInstance
.cdHashType
.c_str();
826 cacheFileResult
.hash
= buildInstance
.cdHash
.c_str();
828 builder
->fileResultBuffers
.push_back({ builder
->fileResultStorage
.size(), true });
829 builder
->fileResultStorage
.emplace_back(cacheFileResult
);
831 // Add a file result for the .map file
832 if ( !buildInstance
.macOSMap
.empty() ) {
833 FileResult cacheFileResult
;
834 cacheFileResult
.version
= 1;
835 cacheFileResult
.path
= buildInstance
.macOSMapPath
.c_str();
836 cacheFileResult
.behavior
= AddFile
;
837 cacheFileResult
.data
= (const uint8_t*)buildInstance
.macOSMap
.data();
838 cacheFileResult
.size
= buildInstance
.macOSMap
.size();
839 cacheFileResult
.hashArch
= buildInstance
.options
->archs
->name();
840 cacheFileResult
.hashType
= buildInstance
.cdHashType
.c_str();
841 cacheFileResult
.hash
= buildInstance
.cdHash
.c_str();
843 builder
->fileResultStorage
.emplace_back(cacheFileResult
);
847 // Copy from the storage to the vector we can return to the API.
848 for (auto &fileResult
: builder
->fileResultStorage
)
849 builder
->fileResults
.push_back(&fileResult
);
850 for (auto &cacheResult
: builder
->cacheResultStorage
)
851 builder
->cacheResults
.push_back(&cacheResult
);
854 // Add entries to tell us to remove all of the dylibs from disk which are in every cache.
855 const size_t numCaches
= builder
->builders
.size();
856 for (const auto& dylibAndCount
: builder
->dylibsInCaches
) {
857 const char* pathToRemove
= dylibAndCount
.first
.c_str();
859 if ( builder
->options
->platform
== Platform::macOS
) {
860 // macOS has to leave the simulator support binaries on disk
861 if ( strcmp(pathToRemove
, "/usr/lib/system/libsystem_kernel.dylib") == 0 )
863 if ( strcmp(pathToRemove
, "/usr/lib/system/libsystem_platform.dylib") == 0 )
865 if ( strcmp(pathToRemove
, "/usr/lib/system/libsystem_pthread.dylib") == 0 )
869 if (dylibAndCount
.second
.size() == numCaches
) {
870 builder
->filesToRemove
.push_back(pathToRemove
);
872 // File is not in every cache, so likely has perhaps only x86_64h slice
873 // but we built both x86_64 and x86_64h caches.
874 // We may still delete it if its in all caches it's eligible for, ie, we
875 // assume the cache builder knows about all possible arch's on the system and
876 // can delete anything it knows can't run
877 bool canDeletePath
= true;
878 for (auto& buildInstance
: builder
->builders
) {
879 if ( dylibAndCount
.second
.count(&buildInstance
) != 0 )
881 // This builder didn't get this image. See if the image was ineligible
882 // based on slide, ie, that dyld at runtime couldn't load this anyway, so
883 // so removing it from disk won't hurt
884 Diagnostics loaderDiag
;
885 const dyld3::GradedArchs
* archs
= buildInstance
.options
->archs
;
886 dyld3::Platform platform
= buildInstance
.options
->platform
;
887 char realerPath
[MAXPATHLEN
];
888 dyld3::closure::LoadedFileInfo fileInfo
= dyld3::MachOAnalyzer::load(loaderDiag
, builder
->fileSystem
,
889 pathToRemove
, *archs
, platform
, realerPath
);
890 if ( (platform
== dyld3::Platform::macOS
) && loaderDiag
.hasError() ) {
891 // Try again with iOSMac
892 loaderDiag
.clearError();
893 fileInfo
= dyld3::MachOAnalyzer::load(loaderDiag
, builder
->fileSystem
,
894 pathToRemove
, *archs
, dyld3::Platform::iOSMac
, realerPath
);
897 // We don't need the file content now, as we only needed to know if this file could be loaded
898 builder
->fileSystem
.unloadFile(fileInfo
);
900 if ( loaderDiag
.hasError() || (fileInfo
.fileContent
== nullptr) ) {
901 // This arch/platform combination couldn't load this path, so we can remove it
905 // This arch was compatible, so the dylib was rejected from this cache for some other reason, eg,
906 // cache overflow. We need to keep it on-disk
907 canDeletePath
= false;
911 builder
->filesToRemove
.push_back(pathToRemove
);
915 // Quit if we had any errors.
916 for (auto& buildInstance
: builder
->builders
) {
917 if (!buildInstance
.errors
.empty())
921 builder
->state
= MRMSharedCacheBuilder::FinishedBuilding
;
927 const char* const* getErrors(const struct MRMSharedCacheBuilder
* builder
, uint64_t* errorCount
) {
928 if (builder
->errors
.empty())
930 *errorCount
= builder
->errors
.size();
931 return builder
->errors
.data();
934 const struct FileResult
* const* getFileResults(struct MRMSharedCacheBuilder
* builder
, uint64_t* resultCount
) {
935 if (builder
->fileResults
.empty())
937 *resultCount
= builder
->fileResults
.size();
938 return builder
->fileResults
.data();
941 const struct CacheResult
* const* getCacheResults(struct MRMSharedCacheBuilder
* builder
, uint64_t* resultCount
) {
942 if (builder
->cacheResults
.empty())
944 *resultCount
= builder
->cacheResults
.size();
945 return builder
->cacheResults
.data();
948 const char* const* getFilesToRemove(const struct MRMSharedCacheBuilder
* builder
, uint64_t* fileCount
) {
949 if (builder
->filesToRemove
.empty())
951 *fileCount
= builder
->filesToRemove
.size();
952 return builder
->filesToRemove
.data();
955 void destroySharedCacheBuilder(struct MRMSharedCacheBuilder
* builder
) {
956 for (auto &indexAndIsDataMalloced
: builder
->fileResultBuffers
) {
957 FileResult
& fileResult
= builder
->fileResultStorage
[indexAndIsDataMalloced
.first
];
958 if (indexAndIsDataMalloced
.second
) {
959 free((void*)fileResult
.data
);
961 vm_deallocate(mach_task_self(), (vm_address_t
)fileResult
.data
, fileResult
.size
);
963 fileResult
.data
= nullptr;