Loading...
cache_builder/mrm_shared_cache_builder.cpp dyld-1122.1.2 dyld-1330
--- dyld/dyld-1122.1.2/cache_builder/mrm_shared_cache_builder.cpp
+++ dyld/dyld-1330/cache_builder/mrm_shared_cache_builder.cpp
@@ -23,16 +23,22 @@
  */
 
 #include "mrm_shared_cache_builder.h"
+#include "Architecture.h"
 #include "Defines.h"
 #include "BuilderFileSystem.h"
 #include "NewSharedCacheBuilder.h"
 #include "Error.h"
 #include "ClosureFileSystem.h"
+#include "ClosureFileSystemNull.h"
 #include "FileUtils.h"
 #include "JSONReader.h"
 #include "JSONWriter.h"
+#include "Misc.h"
+#include "SymbolsCache.h"
+#include "Universal.h"
 #include <pthread.h>
 #include <memory>
+#include <unordered_set>
 #include <vector>
 #include <map>
 #include <sys/stat.h>
@@ -47,18 +53,22 @@
 static const uint64_t kMaxBuildVersion = 3; //The maximum version BuildOptions struct we can support
 
 static const uint32_t MajorVersion = 1;
-static const uint32_t MinorVersion = 3;
+static const uint32_t MinorVersion = 8;
 
 struct BuildInstance {
     std::unique_ptr<cache_builder::BuilderOptions>  options;
-    std::unique_ptr<SharedCacheBuilder>             builder;
+    std::vector<cache_builder::FileAlias>           aliases;
+    std::vector<cache_builder::FileAlias>           intermediateAliases;
     std::string                                     mainCacheFilePath;
+    std::string                                     atlasPath;
     std::vector<const char*>                        errors;
     std::vector<const char*>                        warnings;
     std::vector<std::string>                        errorStrings;   // Owns the data for the errors
     std::vector<std::string>                        warningStrings; // Owns the data for the warnings
     std::vector<CacheBuffer>                        cacheBuffers;
-    std::vector<std::string>                        cachePaths;     // Owns the data for the cache paths
+    std::vector<std::string>                        cachePaths;             // Owns the data for the cache paths
+    std::vector<std::string>                        cacheAgileHashPaths;   // Owns the data for the cache paths for agile signatures
+    std::vector<std::byte>                          atlas;
     std::string                                     loggingPrefix;
     std::string                                     jsonMap;
     std::string                                     mainCacheUUID;
@@ -68,6 +78,7 @@
     std::string                                     macOSMap;       // For compatibility with update_dyld_shared_cache's .map file
     std::string                                     macOSMapPath;   // Owns the string for the path
     std::string                                     cdHashType;     // Owns the data for the cdHashType
+    std::string                                     agileHashType;  // Owns the data for the other cdHashType (if using agile signatures)
 };
 
 struct BuildFileResult {
@@ -105,8 +116,10 @@
 
     std::string dylibOrderFileData;
     std::string dirtyDataOrderFileData;
+    std::string swiftGenericMetadataFileData;
     void* objcOptimizationsFileData;
     size_t objcOptimizationsFileLength;
+    std::string prewarmingMetadataFileData;
 
     // An array of builders and their options as we may have more than one builder for a given device variant.
     std::vector<BuildInstance> builders;
@@ -119,24 +132,14 @@
     // We keep this in a vector to own the data.
     std::vector<FileResult*>                     fileResults;
 
-#if SUPPORT_CACHE_BUILDER_MEMORY_BUFFERS
     // The builder dylib passes back buffers
     std::vector<FileResult_v1>                   fileResultStorage_v1;
-#else
-    // The builder executable gets file descriptors
-    std::vector<FileResult_v2>                   fileResultStorage_v2;
-#endif
 
     // Buffers which were malloc()ed and need free()d
     std::vector<FileBuffer> buffersToFree;
 
     // Buffers which were vm_allocate()d, and need vm_deallocate()d
     std::vector<FileBuffer> buffersToDeallocate;
-
-#if !SUPPORT_CACHE_BUILDER_MEMORY_BUFFERS
-    // Buffers which were open()ed and mmap()ed
-    std::vector<MappedBuffer> buffersToUnmap;
-#endif
 
     // The results from all of the builders
     // We keep this in a vector to own the data.
@@ -146,6 +149,10 @@
 
     // The files to remove.  These are in every copy of the caches we built
     std::vector<const char*> filesToRemove;
+
+    // 1 JSON string per cache we built, with stats
+    std::vector<const char*> stats;
+    std::vector<std::string> statsStorage;
 
     std::vector<const char*> errors;
     std::vector<std::string> errorStorage;
@@ -171,7 +178,7 @@
         va_list list;
         va_start(list, format);
         Diagnostics diag;
-        diag.error(format, list);
+        diag.error(format, va_list_wrap(list));
         va_end(list);
 
         errorStorage.push_back(diag.errorMessage());
@@ -183,7 +190,7 @@
         va_list list;
         va_start(list, format);
         Diagnostics diag;
-        diag.error(format, list);
+        diag.error(format, va_list_wrap(list));
         va_end(list);
 
         warningsStorage.push_back(diag.errorMessage());
@@ -217,6 +224,7 @@
         case Disposition::InternalDevelopment:
         case Disposition::Customer:
         case Disposition::InternalMinDevelopment:
+        case Disposition::SymbolsCache:
             break;
         default:
             builder.error("unknown disposition value");
@@ -247,12 +255,8 @@
     return builder;
 }
 
-bool addFile(struct MRMSharedCacheBuilder* builder, const char* path, uint8_t* data, uint64_t size, FileFlags fileFlags) {
-    return addOnDiskFile(builder, path, data, size, fileFlags, 0, 0);
-}
-
-bool addOnDiskFile(struct MRMSharedCacheBuilder* builder, const char* path, uint8_t* data, uint64_t size, FileFlags fileFlags,
-                   uint64_t inode, uint64_t modTime) {
+static bool addFileImpl(struct MRMSharedCacheBuilder* builder, const char* path, uint8_t* data, uint64_t size, FileFlags fileFlags,
+                        uint64_t inode, uint64_t modTime, const char* projectName) {
     __block bool success = false;
     builder->runSync(^() {
         if (builder->state != MRMSharedCacheBuilder::AcceptingFiles) {
@@ -291,12 +295,52 @@
                 builder->objcOptimizationsFileLength = size;
                 success = true;
                 return;
+            case SwiftGenericMetadataFile:
+                builder->swiftGenericMetadataFileData = std::string((char*)data, size);
+                success = true;
+                return;
+            case OptimizationFile: {
+                // TODO: Remove DylibOrderFile..SwiftGenericMetadataFile once image assembly
+                // passes this for all files from the OrderFiles project
+                CString leafName = CString(path).leafName();
+                if ( leafName == "dylib-order.txt" ) {
+                    builder->dylibOrderFileData = std::string((char*)data, size);
+                    success = true;
+                    return;
+                }
+                if ( leafName == "dirty-data-segments-order.txt" ) {
+                    builder->dirtyDataOrderFileData = std::string((char*)data, size);
+                    success = true;
+                    return;
+                }
+                if ( leafName == "shared-cache-objc-optimizations.json" ) {
+                    builder->objcOptimizationsFileData = data;
+                    builder->objcOptimizationsFileLength = size;
+                    success = true;
+                    return;
+                }
+                if ( leafName == "swift-generic-metadata.json" ) {
+                    builder->swiftGenericMetadataFileData = std::string((char*)data, size);
+                    success = true;
+                    return;
+                }
+                if ( leafName == "prewarming-metadata.json" ) {
+                    builder->prewarmingMetadataFileData = std::string((char*)data, size);
+                    success = true;
+                    return;
+                }
+                // Skip this file as image assembly will probably just give us all files in a given
+                // directory and that might include new/unrelated content
+                builder->warning("unknown optimization file path: %s", path);
+                success = true;
+                return;
+            }
             default:
                 builder->error("unknown file flags value");
                 break;
         }
         Diagnostics diag;
-        if (!builder->fileSystem.addFile(path, data, size, diag, fileFlags, inode, modTime)) {
+        if (!builder->fileSystem.addFile(path, data, size, diag, fileFlags, inode, modTime, projectName)) {
             builder->errorStorage.push_back(diag.errorMessage());
             builder->errors.push_back(builder->errorStorage.back().data());
             return;
@@ -304,6 +348,19 @@
         success = true;
     });
     return success;
+}
+
+bool addFile(struct MRMSharedCacheBuilder* builder, const char* path, uint8_t* data, uint64_t size, FileFlags fileFlags) {
+    return addFileImpl(builder, path, data, size, fileFlags, 0, 0, "");
+}
+
+bool addFile_v2(struct MRMSharedCacheBuilder* builder, const char* path, uint8_t* data, uint64_t size, FileFlags fileFlags, const char* projectName) {
+    return addFileImpl(builder, path, data, size, fileFlags, 0, 0, projectName);
+}
+
+bool addOnDiskFile(struct MRMSharedCacheBuilder* builder, const char* path, uint8_t* data, uint64_t size, FileFlags fileFlags,
+                   uint64_t inode, uint64_t modTime) {
+    return addFileImpl(builder, path, data, size, fileFlags, inode, modTime, "");
 }
 
 bool addSymlink(struct MRMSharedCacheBuilder* builder, const char* fromPath, const char* toPath) {
@@ -334,7 +391,7 @@
 }
 
 static cache_builder::LocalSymbolsMode platformExcludeLocalSymbols(Platform platform) {
-    if ( dyld3::MachOFile::isSimulatorPlatform((dyld3::Platform)platform) )
+    if ( mach_o::Platform((uint32_t)platform).isSimulator() )
         return cache_builder::LocalSymbolsMode::keep;
     if ( (platform == Platform::macOS) || (platform == Platform::iOSMac) )
         return cache_builder::LocalSymbolsMode::keep;
@@ -369,6 +426,8 @@
             return "Customer";
         case Disposition::InternalMinDevelopment:
             return "InternalMinDevelopment";
+        case Disposition::SymbolsCache:
+            return "SymbolsCache";
     }
 }
 
@@ -402,22 +461,44 @@
     return v3->printStats;
 }
 
+static bool debugIMPCaches(const BuildOptions_v1* options) {
+    // Old builds just don't print this verbose output
+    if ( options->version < 3 ) {
+        return false;
+    }
+
+    const BuildOptions_v3* v3 = (const BuildOptions_v3*)options;
+    return v3->verboseIMPCaches;
+}
+
+static bool debugCacheLayout(const BuildOptions_v1* options) {
+    // Old builds just don't print this verbose output
+    if ( options->version < 3 ) {
+        return false;
+    }
+
+    const BuildOptions_v3* v3 = (const BuildOptions_v3*)options;
+    return v3->verboseCacheLayout;
+}
+
 // This is a JSON file containing the list of classes for which
 // we should try to build IMP caches.
-static dyld3::json::Node parseObjcOptimizationsFile(Diagnostics& diags, const void* data, size_t length) {
+static json::Node parseObjcOptimizationsFile(Diagnostics& diags, const void* data, size_t length) {
     if ( data == nullptr )
-        return dyld3::json::Node();
-    return dyld3::json::readJSON(diags, data, length);
+        return json::Node();
+    return json::readJSON(diags, data, length, false /* useJSON5 */);
 }
 
 static cache_builder::CacheKind getCacheKind(const BuildOptions_v1* options)
 {
-    // Work out what kind of cache we are building.  macOS/driverKit are always development
-    if ( (options->platform == macOS) || (options->platform == driverKit) )
+    // Work out what kind of cache we are building.  macOS/driverKit/exclaveKit are always development
+    if (   (options->platform == macOS)
+        || (options->platform == driverKit)
+        || mach_o::Platform(options->platform).isExclaveKit() )
         return cache_builder::CacheKind::development;
 
     // Sims are always development
-    if ( dyld3::MachOFile::isSimulatorPlatform((dyld3::Platform)options->platform) )
+    if ( mach_o::Platform(options->platform).isSimulator() )
         return cache_builder::CacheKind::development;
 
     // iOS is always universal. If building for InternalMinDevelopment, we'll build universal
@@ -443,6 +524,8 @@
             return false;
         case Disposition::InternalMinDevelopment:
             return true;
+        case Disposition::SymbolsCache:
+            return false;
     }
 
     return true;
@@ -466,9 +549,19 @@
             return true;
         case Disposition::InternalMinDevelopment:
             return false;
+        case Disposition::SymbolsCache:
+            return false;
     }
 
     return true;
+}
+
+static std::string cacheFileName(const char* arch, bool isSimulator) {
+    if ( isSimulator ) {
+        return std::string("dyld_sim_shared_cache_") + arch;
+    } else {
+        return std::string("dyld_shared_cache_") + arch;
+    }
 }
 
 static bool createBuilders(struct MRMSharedCacheBuilder* builder)
@@ -500,11 +593,13 @@
         if ( strcmp(builder->options->archs[i], "i386") == 0 )
             continue;
 
-        // Add a driverKit suffix.  Note we don't need to add .development suffixes any
+        // Add a driverKit/exclaveKit suffix.  Note we don't need to add .development suffixes any
         // more as the universal caches don't build customer and development seperately
         const char *loggingSuffix = "";
         if ( builder->options->platform == Platform::driverKit )
             loggingSuffix = ".driverKit";
+        if ( mach_o::Platform(builder->options->platform).isExclaveKit() )
+            loggingSuffix = ".exclaveKit";
 
         std::string loggingPrefix = "";
         loggingPrefix += std::string(builder->options->deviceName);
@@ -512,18 +607,22 @@
         loggingPrefix += std::string(".") + builder->options->archs[i];
         loggingPrefix += loggingSuffix;
 
+        std::string mainCacheFileName;
         std::string runtimePath;
-        if ( dyld3::MachOFile::isSimulatorPlatform((dyld3::Platform)builder->options->platform) ) {
+        if ( mach_o::Platform(builder->options->platform).isSimulator() ) {
             // Sim caches are written exactly where instructed, without adding any directory structure
-            runtimePath = runtimePath + "dyld_sim_shared_cache_" + builder->options->archs[i];
+            mainCacheFileName = cacheFileName(builder->options->archs[i], true);
+            runtimePath = mainCacheFileName;
         } else {
+            mainCacheFileName = cacheFileName(builder->options->archs[i], false);
             if ( builder->options->platform == Platform::macOS )
-                runtimePath = MACOSX_MRM_DYLD_SHARED_CACHE_DIR;
+                runtimePath = MACOSX_MRM_DYLD_SHARED_CACHE_DIR + mainCacheFileName;
             else if ( builder->options->platform == Platform::driverKit )
-                runtimePath = DRIVERKIT_DYLD_SHARED_CACHE_DIR;
+                runtimePath = DRIVERKIT_DYLD_SHARED_CACHE_DIR + mainCacheFileName;
+            else if ( mach_o::Platform(builder->options->platform).isExclaveKit() )
+                runtimePath = EXCLAVEKIT_DYLD_SHARED_CACHE_DIR + mainCacheFileName;
             else
-                runtimePath = IPHONE_DYLD_SHARED_CACHE_DIR;
-            runtimePath = runtimePath + "dyld_shared_cache_" + builder->options->archs[i];
+                runtimePath = IPHONE_DYLD_SHARED_CACHE_DIR + mainCacheFileName;
         }
 
         bool dylibsRemovedFromDisk  = filesRemovedFromDisk(builder->options);
@@ -541,12 +640,17 @@
             forceDevelopmentSubCacheSuffix = true;
 
         auto options = std::make_unique<cache_builder::BuilderOptions>(builder->options->archs[i],
-                                                                       (dyld3::Platform)builder->options->platform,
+                                                                       builder->options->platform,
                                                                        dylibsRemovedFromDisk, isLocallyBuiltCache,
-                                                                       cacheKind, forceDevelopmentSubCacheSuffix);
-
+                                                                       cacheKind, forceDevelopmentSubCacheSuffix,
+                                                                       builder->options->updateName,
+                                                                       builder->options->deviceName);
+
+        options->mainCacheFileName           = mainCacheFileName;
         options->logPrefix                   = loggingPrefix;
         options->debug                       = builder->options->verboseDiagnostics;
+        options->debugIMPCaches              = debugIMPCaches(builder->options);
+        options->debugCacheLayout            = debugCacheLayout(builder->options);
         options->timePasses                  = options->debug ? true : timePasses(builder->options);
         options->stats                       = options->debug ? true : printStats(builder->options);
         options->dylibOrdering               = parseOrderFile(builder->dylibOrderFileData);
@@ -554,42 +658,17 @@
         options->objcOptimizations           = parseObjcOptimizationsFile(diag, builder->objcOptimizationsFileData,
                                                                           builder->objcOptimizationsFileLength);
         options->localSymbolsMode            = excludeLocalSymbols(builder->options);
+        options->swiftGenericMetadataFile    = builder->swiftGenericMetadataFileData;
+        options->prewarmingOptimizations     = builder->prewarmingMetadataFileData;
 
         BuildInstance buildInstance;
-        buildInstance.options           = std::move(options);
-        buildInstance.builder           = std::make_unique<SharedCacheBuilder>(*buildInstance.options.get(), builder->fileSystem);
-        buildInstance.mainCacheFilePath = runtimePath;
+        buildInstance.options               = std::move(options);
+        buildInstance.aliases               = aliases;
+        buildInstance.intermediateAliases   = intermediateAliases;
+        buildInstance.mainCacheFilePath     = runtimePath;
+        buildInstance.atlasPath             = runtimePath + ".atlas";
 
         builder->builders.push_back(std::move(buildInstance));
-    }
-
-    // Add all the input files
-    __block std::vector<cache_builder::InputFile> inputFiles;
-    builder->fileSystem.forEachFileInfo(^(const char* path, const void* buffer, size_t bufferSize,
-                                          FileFlags fileFlags, uint64_t inode, uint64_t modTime) {
-        switch (fileFlags) {
-            case FileFlags::NoFlags:
-            case FileFlags::MustBeInCache:
-            case FileFlags::ShouldBeExcludedFromCacheIfUnusedLeaf:
-            case FileFlags::RequiredClosure:
-                break;
-            case FileFlags::DylibOrderFile:
-            case FileFlags::DirtyDataOrderFile:
-            case FileFlags::ObjCOptimizationsFile:
-                builder->error("Order files should not be in the file system");
-                return;
-        }
-
-        for (auto& buildInstance : builder->builders) {
-            SharedCacheBuilder* cacheBuilder = buildInstance.builder.get();
-            cacheBuilder->addFile(buffer, bufferSize, path, inode, modTime);
-        }
-    });
-
-    // Add resolved aliases (symlinks)
-    for (auto& buildInstance : builder->builders) {
-        SharedCacheBuilder* cacheBuilder = buildInstance.builder.get();
-        cacheBuilder->setAliases(aliases, intermediateAliases);
     }
 
     return true;
@@ -598,34 +677,103 @@
 static void runBuilders(struct MRMSharedCacheBuilder* builder)
 {
     for (auto& buildInstance : builder->builders) {
-        std::unique_ptr<SharedCacheBuilder> cacheBuilder = std::move(buildInstance.builder);
-        Error error = cacheBuilder->build();
+        // The build might overflow, so loop until we don't error from overflow
+        std::string              swiftPrespecializedDylibBuildError;
+        std::vector<std::string> evictedDylibs;
+        std::unordered_set<std::string> evictedDylibsSet;
+        __block std::unique_ptr<SharedCacheBuilder> cacheBuilder;
+        Error error;
+
+        // Note down when we start so we can emit it in stats().
+        // This is outside the loop to include potential time in eviction and rebuilds
+        uint64_t startTimeNanos = clock_gettime_nsec_np(CLOCK_UPTIME_RAW);
+        while ( true ) {
+            cacheBuilder = std::make_unique<SharedCacheBuilder>(*buildInstance.options.get(), builder->fileSystem);
+
+            // Add all the input files
+            __block std::vector<cache_builder::InputFile> inputFiles;
+            builder->fileSystem.forEachFileInfo(^(const char* path, const void* buffer, size_t bufferSize,
+                                                  FileFlags fileFlags, uint64_t inode, uint64_t modTime,
+                                                  const char* projectName) {
+                switch (fileFlags) {
+                    case FileFlags::NoFlags:
+                    case FileFlags::MustBeInCache:
+                    case FileFlags::ShouldBeExcludedFromCacheIfUnusedLeaf:
+                    case FileFlags::RequiredClosure:
+                        break;
+                    case FileFlags::DylibOrderFile:
+                    case FileFlags::DirtyDataOrderFile:
+                    case FileFlags::ObjCOptimizationsFile:
+                    case FileFlags::SwiftGenericMetadataFile:
+                    case FileFlags::OptimizationFile:
+                        builder->error("Order files should not be in the file system");
+                        return;
+                }
+
+                cacheBuilder->addFile(buffer, bufferSize, path, inode, modTime, evictedDylibsSet.count(path));
+            });
+
+            // Add resolved aliases (symlinks)
+            cacheBuilder->setAliases(buildInstance.aliases, buildInstance.intermediateAliases);
+
+            error = cacheBuilder->build();
+
+            // Get result buffers, even if there's an error.  That way we'll free them
+            cacheBuilder->getResults(buildInstance.cacheBuffers, buildInstance.atlas);
+
+            if ( !error.hasError() )
+                break;
+
+            // try without Swift metadata dylib if building it failed
+            bool retryWithoutSwiftMetadata = false;
+            if ( !cacheBuilder->getSwiftPrespecializedDylibBuildError().empty() ) {
+                assert(!buildInstance.options->swiftGenericMetadataFile.empty()
+                        && "Swift prespecialization build error even with an empty metadata file");
+                swiftPrespecializedDylibBuildError = std::string(cacheBuilder->getSwiftPrespecializedDylibBuildError());
+                buildInstance.options->swiftGenericMetadataFile.clear();
+                retryWithoutSwiftMetadata = true;
+            }
+
+            // We have an error. If its cache overflow, then we can try again, with some evicted dylibs
+            bool retryEvictedDylibs = false;
+            std::span<const std::string_view> newEvictedDylibs = cacheBuilder->getEvictedDylibs();
+            if ( !newEvictedDylibs.empty() ) {
+                evictedDylibs.insert(evictedDylibs.end(), newEvictedDylibs.begin(), newEvictedDylibs.end());
+                evictedDylibsSet.insert(newEvictedDylibs.begin(), newEvictedDylibs.end());
+                retryEvictedDylibs = true;
+            }
+
+            bool retry = retryEvictedDylibs || retryWithoutSwiftMetadata;
+            if ( !retry ) {
+                // Error wasn't eviction, nor Swift metadata build. Break out an handle it as a fatal error
+                break;
+            }
+
+            // Cache eviction happened. Note down the bad dylibs, and try again
+            // Note we should never have buffer data to free at this point as eviction should be
+            // determined before buffers are allocated
+            for ( const CacheBuffer& buffer : buildInstance.cacheBuffers ) {
+                assert(buffer.bufferData == nullptr);
+            }
+            buildInstance.cacheBuffers.clear();
+        }
 
         buildInstance.loggingPrefix = cacheBuilder->developmentLoggingPrefix();
         buildInstance.customerLoggingPrefix = cacheBuilder->customerLoggingPrefix();
 
-        // Get result buffers, even if there's an error.  That way we'll free them
-        cacheBuilder->getResults(buildInstance.cacheBuffers);
-
         // Track all buffers to be freed/unmapped (see allocateSubCacheBuffers() for allocation)
         for ( const CacheBuffer& buffer : buildInstance.cacheBuffers ) {
-#if SUPPORT_CACHE_BUILDER_MEMORY_BUFFERS
             // In MRM, we vm_allocated
             builder->buffersToDeallocate.emplace_back((FileBuffer){ buffer.bufferData, buffer.bufferSize });
-#else
-            // In the local builder, we mmap()ed
-            builder->buffersToUnmap.emplace_back((MappedBuffer) {
-                buffer.bufferData,
-                buffer.bufferSize,
-                buffer.fd,
-                buffer.tempPath
-            });
-#endif
         }
 
         if ( error.hasError() ) {
             // First put the error in to a vector to own it
             buildInstance.errorStrings.push_back(error.message());
+
+            cacheBuilder->forEachError(^(const std::string_view &str) {
+                buildInstance.errorStrings.push_back(std::string(str));
+            });
 
             // Then copy to a vector to reference the owner
             buildInstance.errors.reserve(buildInstance.errorStrings.size());
@@ -634,6 +782,14 @@
 
             // First put the warnings in to a vector to own them.
             if ( builder->options->verboseDiagnostics ) {
+                // Add cache eviction warnings, if any
+                for ( std::string path : evictedDylibs ) {
+                    std::string reason = "Dylib located at '" + path + "' not placed in shared cache because: cache overflow";
+                    buildInstance.warningStrings.push_back(reason);
+                }
+                if ( !swiftPrespecializedDylibBuildError.empty() )
+                    buildInstance.warningStrings.push_back("Couldn't build Swift prespecialized metadata dylib: " + swiftPrespecializedDylibBuildError);
+
                 cacheBuilder->forEachWarning(^(const std::string_view &str) {
                     buildInstance.warningStrings.push_back(std::string(str));
                 });
@@ -659,8 +815,8 @@
                 return true;
             });
 
-            if ( (buildInstance.options->platform == dyld3::Platform::macOS)
-                || dyld3::MachOFile::isSimulatorPlatform(buildInstance.options->platform) ) {
+            if ( (buildInstance.options->platform == Platform::macOS)
+                || buildInstance.options->platform.isSimulator() ) {
                 // For compatibility with update_dyld_shared_cache/update_dyld_sim_shared_cache, put a .map file next to the shared cache
                 buildInstance.macOSMap = cacheBuilder->getMapFileBuffer();
                 buildInstance.macOSMapPath = buildInstance.mainCacheFilePath + ".map";
@@ -675,6 +831,15 @@
 
             // Only add warnings if the build was good
             // First put the warnings in to a vector to own them.
+
+            // Add cache eviction warnings, if any
+            for ( std::string path : evictedDylibs ) {
+                std::string reason = "Dylib located at '" + path + "' not placed in shared cache because: cache overflow";
+                buildInstance.warningStrings.push_back(reason);
+            }
+            if ( !swiftPrespecializedDylibBuildError.empty() )
+                buildInstance.warningStrings.push_back("Couldn't build Swift prespecialized metadata dylib: " + swiftPrespecializedDylibBuildError);
+
             cacheBuilder->forEachWarning(^(const std::string_view &str) {
                 buildInstance.warningStrings.push_back(std::string(str));
             });
@@ -693,6 +858,7 @@
                     break;
                 case DyldSharedCache::Agile:
                     buildInstance.cdHashType = "sha1";
+                    buildInstance.agileHashType = "sha256";
                     break;
             }
 
@@ -703,7 +869,16 @@
             cacheBuilder->forEachCacheSymlink(^(const std::string_view &path) {
                 builder->dylibsInCaches[std::string(path)].insert(&buildInstance);
             });
-        }
+
+            // Track cache stats
+            builder->statsStorage.push_back(cacheBuilder->stats(startTimeNanos));
+        }
+    }
+
+    // Make the stats vector from the storage
+    if ( !builder->statsStorage.empty() ) {
+        for ( std::string& str : builder->statsStorage )
+            builder->stats.push_back(str.data());
     }
 }
 
@@ -756,71 +931,72 @@
         if (!buildInstance.errors.empty())
             continue;
 
-        for ( const CacheBuffer& buffer : buildInstance.cacheBuffers )
+        for ( const CacheBuffer& buffer : buildInstance.cacheBuffers ) {
             buildInstance.cachePaths.push_back(buildInstance.mainCacheFilePath + buffer.cacheFileSuffix);
+            if ( !buildInstance.agileHashType.empty() ) {
+                buildInstance.cacheAgileHashPaths.push_back(buildInstance.cachePaths.back() + ".cdhash");
+            }
+        }
 
         uint32_t cacheIndex = 0;
         for ( const CacheBuffer& cacheBuffer : buildInstance.cacheBuffers ) {
-#if SUPPORT_CACHE_BUILDER_MEMORY_BUFFERS
-            FileResult_v1 cacheFileResult;
-            cacheFileResult.version          = 1;
-            cacheFileResult.path             = buildInstance.cachePaths[cacheIndex].c_str();
-            cacheFileResult.behavior         = AddFile;
-            cacheFileResult.data             = cacheBuffer.bufferData;
-            cacheFileResult.size             = cacheBuffer.bufferSize;
-            cacheFileResult.hashArch         = buildInstance.options->archs.name();
-            cacheFileResult.hashType         = buildInstance.cdHashType.c_str();
-            cacheFileResult.hash             = cacheBuffer.cdHash.c_str();
-
-            builder->fileResultStorage_v1.emplace_back(cacheFileResult);
-#else
-            FileResult_v2 cacheFileResult;
-            cacheFileResult.version          = 2;
-            cacheFileResult.path             = buildInstance.cachePaths[cacheIndex].c_str();
-            cacheFileResult.behavior         = AddFile;
-            cacheFileResult.data             = cacheBuffer.bufferData;
-            cacheFileResult.size             = cacheBuffer.bufferSize;
-            cacheFileResult.hashArch         = buildInstance.options->archs.name();
-            cacheFileResult.hashType         = buildInstance.cdHashType.c_str();
-            cacheFileResult.hash             = cacheBuffer.cdHash.c_str();
-            cacheFileResult.fd               = cacheBuffer.fd;
-            cacheFileResult.tempFilePath     = cacheBuffer.tempPath.c_str();
-
-            builder->fileResultStorage_v2.emplace_back(cacheFileResult);
-#endif
+            {
+                FileResult_v1 cacheFileResult;
+                cacheFileResult.version          = 1;
+                cacheFileResult.path             = buildInstance.cachePaths[cacheIndex].c_str();
+                cacheFileResult.behavior         = AddFile;
+                cacheFileResult.data             = cacheBuffer.bufferData;
+                cacheFileResult.size             = cacheBuffer.bufferSize;
+                cacheFileResult.hashArch         = buildInstance.options->arch.name();
+                cacheFileResult.hashType         = buildInstance.cdHashType.c_str();
+                cacheFileResult.hash             = cacheBuffer.cdHash.c_str();
+
+                builder->fileResultStorage_v1.emplace_back(cacheFileResult);
+            }
+
+            // Generate a dummy file for the agile cdHash if we have it, this gets it in to the trust cache
+            if ( !buildInstance.agileHashType.empty() ) {
+                FileResult_v1 cacheFileResult;
+                cacheFileResult.version          = 1;
+                cacheFileResult.path             = buildInstance.cacheAgileHashPaths[cacheIndex].c_str();
+                cacheFileResult.behavior         = AddFile;
+                cacheFileResult.data             = (const uint8_t*)"cdhash";
+                cacheFileResult.size             = strlen("cdhash");
+                cacheFileResult.hashArch         = buildInstance.options->arch.name();
+                cacheFileResult.hashType         = buildInstance.agileHashType.c_str();
+                cacheFileResult.hash             = cacheBuffer.agilecdHash.c_str();
+
+                builder->fileResultStorage_v1.emplace_back(cacheFileResult);
+            }
             ++cacheIndex;
         }
+
+        FileResult_v1 arlasFileResult;
+        arlasFileResult.version     = 1;
+        arlasFileResult.path        = buildInstance.atlasPath.c_str();
+        arlasFileResult.behavior    = AddFile;
+        arlasFileResult.data        = (const uint8_t*)&buildInstance.atlas[0];
+        arlasFileResult.size        = buildInstance.atlas.size();
+        arlasFileResult.hashArch    = buildInstance.options->arch.name();
+        arlasFileResult.hashType    = buildInstance.cdHashType.c_str();
+        arlasFileResult.hash        = buildInstance.cacheBuffers.front().cdHash.c_str();;
+
+        builder->fileResultStorage_v1.emplace_back(arlasFileResult);
 
         // Add a file result for the .map file
         // FIXME: We only emit a single map file right now.
         if ( !buildInstance.macOSMap.empty() ) {
-#if SUPPORT_CACHE_BUILDER_MEMORY_BUFFERS
             FileResult_v1 cacheFileResult;
             cacheFileResult.version     = 1;
             cacheFileResult.path        = buildInstance.macOSMapPath.c_str();
             cacheFileResult.behavior    = AddFile;
             cacheFileResult.data        = (const uint8_t*)buildInstance.macOSMap.data();
             cacheFileResult.size        = buildInstance.macOSMap.size();
-            cacheFileResult.hashArch    = buildInstance.options->archs.name();
+            cacheFileResult.hashArch    = buildInstance.options->arch.name();
             cacheFileResult.hashType    = buildInstance.cdHashType.c_str();
             cacheFileResult.hash        = buildInstance.cacheBuffers.front().cdHash.c_str();;
 
             builder->fileResultStorage_v1.emplace_back(cacheFileResult);
-#else
-            FileResult_v2 cacheFileResult;
-            cacheFileResult.version          = 2;
-            cacheFileResult.path             = buildInstance.macOSMapPath.c_str();
-            cacheFileResult.behavior         = AddFile;
-            cacheFileResult.data             = (const uint8_t*)buildInstance.macOSMap.data();
-            cacheFileResult.size             = buildInstance.macOSMap.size();
-            cacheFileResult.hashArch         = buildInstance.options->archs.name();
-            cacheFileResult.hashType         = buildInstance.cdHashType.c_str();
-            cacheFileResult.hash             = buildInstance.cacheBuffers.front().cdHash.c_str();
-            cacheFileResult.fd               = 0;
-            cacheFileResult.tempFilePath     = nullptr;
-
-            builder->fileResultStorage_v2.emplace_back(cacheFileResult);
-#endif
         }
     }
 }
@@ -877,9 +1053,11 @@
                 // so removing it from disk won't hurt
                 Diagnostics loaderDiag;
                 const bool  isOSBinary = false;
-                const dyld3::GradedArchs& archs = buildInstance.options->archs;
-                dyld3::Platform platform = buildInstance.options->platform;
-                if ( const auto* mf = dyld3::MachOFile::compatibleSlice(loaderDiag, buffer, bufferSize,
+                const mach_o::GradedArchitectures& archs = buildInstance.options->gradedArchs;
+                mach_o::Platform platform = buildInstance.options->platform;
+                uint64_t sliceOffset = 0;
+                uint64_t sliceSize = 0;
+                if ( const auto* mf = dyld3::MachOFile::compatibleSlice(loaderDiag, sliceOffset, sliceSize, buffer, bufferSize,
                                                                         pathToRemove, platform,
                                                                         isOSBinary, archs) ) {
                     // This arch was compatible, so the dylib was rejected from this cache for some other reason, eg,
@@ -891,11 +1069,11 @@
                 }
 
                 // Check iOSMac, just in case we couldn't load the slice on macOS
-                if ( (platform == dyld3::Platform::macOS) && loaderDiag.hasError() ) {
+                if ( (platform == mach_o::Platform::macOS) && loaderDiag.hasError() ) {
                     loaderDiag.clearError();
 
-                    if ( const auto* mf = dyld3::MachOFile::compatibleSlice(loaderDiag, buffer, bufferSize,
-                                                                            pathToRemove, dyld3::Platform::iOSMac,
+                    if ( const auto* mf = dyld3::MachOFile::compatibleSlice(loaderDiag, sliceOffset, sliceSize, buffer, bufferSize,
+                                                                            pathToRemove, mach_o::Platform::macCatalyst,
                                                                             isOSBinary, archs) ) {
                         // This arch was compatible, so the dylib was rejected from this cache for some other reason, eg,
                         // cache overflow.  We need to keep it on-disk
@@ -912,7 +1090,189 @@
     }
 }
 
+static bool runSymbolsCacheBuilder(struct MRMSharedCacheBuilder* builder) {
+    __block bool success = false;
+    builder->runSync(^() {
+        __block SymbolsCache::ArchPlatforms archPlatforms;
+        if ( builder->options->platform == driverKit ) {
+            // Note Image Assembly might not know which archs/platforms to build as a single DylibCache could
+            // have different archs for different platforms, like userland vs driverKit vs exclaves.
+            builder->fileSystem.forEachFileInfo(^(const char* path, const void* buffer, size_t bufferSize,
+                                                  FileFlags fileFlags, uint64_t inode, uint64_t modTime,
+                                                  const char* projectName) {
+                if ( !strcmp(path, "/System/DriverKit/usr/lib/libSystem.dylib")
+                    || !strcmp(path, "/System/DriverKit/usr/lib/libSystem.B.dylib") ) {
+                    const std::span<uint8_t> bufferSpan = { (uint8_t*)buffer, bufferSize };
+                    mach_o::Error parseErr = mach_o::forEachHeader(bufferSpan, path,
+                                                                   ^(const mach_o::Header* mh, size_t sliceHeader, bool &stop) {
+                        mach_o::PlatformAndVersions pvs = mh->platformAndVersions();
+                        if ( pvs.platform.empty() )
+                            return;
+
+                        archPlatforms[mh->archName()].push_back(pvs.platform);
+                    });
+                    if ( parseErr ) {
+                        builder->error("Cannot build symbols cache because: %s", parseErr.message());
+                    }
+                }
+            });
+        } else if ( mach_o::Platform(builder->options->platform).isExclaveKit() ) {
+            // Note Image Assembly might not know which archs/platforms to build as a single DylibCache could
+            // have different archs for different platforms, like userland vs driverKit vs exclaves.
+            builder->fileSystem.forEachFileInfo(^(const char* path, const void* buffer, size_t bufferSize,
+                                                  FileFlags fileFlags, uint64_t inode, uint64_t modTime,
+                                                  const char* projectName) {
+                if ( !strcmp(path, "/System/ExclaveKit/usr/lib/libSystem.dylib") ) {
+                    const std::span<uint8_t> bufferSpan = { (uint8_t*)buffer, bufferSize };
+                    mach_o::Error parseErr = mach_o::forEachHeader(bufferSpan, path,
+                                                                   ^(const mach_o::Header* mh, size_t sliceHeader, bool &stop) {
+                        mach_o::PlatformAndVersions pvs = mh->platformAndVersions();
+                        if ( pvs.platform.empty() )
+                            return;
+
+                        archPlatforms[mh->archName()].push_back(pvs.platform);
+                    });
+                    if ( parseErr ) {
+                        builder->error("Cannot build symbols cache because: %s", parseErr.message());
+                    }
+                }
+            });
+        } else {
+            // Note Image Assembly might not know which archs/platforms to build as a single DylibCache could
+            // have different archs for different platforms, like userland vs driverKit vs exclaves.
+            builder->fileSystem.forEachFileInfo(^(const char* path, const void* buffer, size_t bufferSize,
+                                                  FileFlags fileFlags, uint64_t inode, uint64_t modTime,
+                                                  const char* projectName) {
+                if ( !strcmp(path, "/usr/lib/libSystem.dylib")
+                    || !strcmp(path, "/usr/lib/libSystem.B.dylib")
+                    || !strcmp(path, "/System/DriverKit/usr/lib/libSystem.dylib")
+                    || !strcmp(path, "/System/DriverKit/usr/lib/libSystem.B.dylib")
+                    || !strcmp(path, "/System/ExclaveKit/usr/lib/libSystem.dylib") ) {
+                    const std::span<uint8_t> bufferSpan = { (uint8_t*)buffer, bufferSize };
+                    mach_o::Error parseErr = mach_o::forEachHeader(bufferSpan, path,
+                                                                   ^(const mach_o::Header* mh, size_t sliceHeader, bool &stop) {
+                        mach_o::PlatformAndVersions pvs = mh->platformAndVersions();
+                        if ( pvs.platform.empty() )
+                            return;
+
+                        // HACK: Pretend zippered are macOS, so that the database doesn't have to care about zippering
+                        mach_o::Platform platform;
+                        if ( (pvs.platform == mach_o::Platform::zippered) || (pvs.platform == mach_o::Platform::macCatalyst) )
+                            platform = mach_o::Platform::macOS;
+                        else
+                            platform = pvs.platform;
+
+                        archPlatforms[mh->archName()].push_back(platform);
+                    });
+                    if ( parseErr ) {
+                        builder->error("Cannot build symbols cache because: %s", parseErr.message());
+                    }
+                }
+            });
+        }
+
+        __block class SymbolsCache cache;
+
+        if ( mach_o::Error err = cache.create() ) {
+            builder->error("Cannot create symbols cache because: %s", err.message());
+            return;
+        }
+
+        __block bool gotFileErr = false;
+        __block std::vector<SymbolsCacheBinary> binaries;
+        builder->fileSystem.forEachFileInfo(^(const char* path, const void* buffer, size_t bufferSize,
+                                              FileFlags fileFlags, uint64_t inode, uint64_t modTime,
+                                              const char* projectName) {
+            if ( gotFileErr )
+                return;
+            switch (fileFlags) {
+                case FileFlags::NoFlags:
+                case FileFlags::MustBeInCache:
+                case FileFlags::ShouldBeExcludedFromCacheIfUnusedLeaf:
+                case FileFlags::RequiredClosure:
+                    break;
+                case FileFlags::DylibOrderFile:
+                case FileFlags::DirtyDataOrderFile:
+                case FileFlags::ObjCOptimizationsFile:
+                case FileFlags::SwiftGenericMetadataFile:
+                case FileFlags::OptimizationFile:
+                    builder->error("Order files should not be in the file system");
+                    return;
+            }
+
+            if ( mach_o::Error err = cache.makeBinaries(archPlatforms, builder->fileSystem, 
+                                                        buffer, bufferSize, path, projectName,
+                                                        binaries) ) {
+                builder->error("Cannot build symbols cache because: %s", err.message());
+                gotFileErr = true;
+                return;
+            }
+        });
+
+        if ( gotFileErr )
+            return;
+
+        if ( mach_o::Error err = cache.addBinaries(binaries) ) {
+            builder->error("Cannot build symbols cache because: %s", err.message());
+            return;
+        }
+
+        const uint8_t* buffer = nullptr;
+        uint64_t bufferSize = 0;
+        if ( mach_o::Error err = cache.serialize(buffer, bufferSize) ) {
+            builder->error("Cannot serialize symbols cache because: %s", err.message());
+            return;
+        }
+
+        CacheResult cacheBuildResult;
+        cacheBuildResult.version              = 1;
+        cacheBuildResult.loggingPrefix        = "symbols-cache";
+        cacheBuildResult.deviceConfiguration  = "symbols-cache";
+        cacheBuildResult.warnings             = nullptr;
+        cacheBuildResult.numWarnings          = 0;
+        cacheBuildResult.errors               = nullptr;
+        cacheBuildResult.numErrors            = 0;
+        cacheBuildResult.uuidString           = "";
+        cacheBuildResult.mapJSON              = "";
+        cacheBuildResult.warnings             = nullptr;
+        cacheBuildResult.numWarnings          = 0;
+        cacheBuildResult.errors               = builder->errors.empty() ? nullptr : builder->errors.data();
+        cacheBuildResult.numErrors            = builder->errors.size();
+
+        builder->cacheResultStorage.push_back(cacheBuildResult);
+        builder->cacheResults.push_back(&builder->cacheResultStorage.back());
+
+        const char* resultPath = MACOSX_MRM_DYLD_SHARED_CACHE_DIR "dyld_symbols.db";;
+        if ( builder->options->platform == driverKit )
+            resultPath = DRIVERKIT_DYLD_SHARED_CACHE_DIR "dyld_symbols.db";
+        else if ( mach_o::Platform(builder->options->platform).isExclaveKit() )
+            resultPath = EXCLAVEKIT_DYLD_SHARED_CACHE_DIR "dyld_symbols.db";
+
+        FileResult_v1 cacheFileResult;
+        cacheFileResult.version          = 1;
+        cacheFileResult.path             = resultPath;
+        cacheFileResult.behavior         = AddFile;
+        cacheFileResult.data             = buffer;
+        cacheFileResult.size             = bufferSize;
+        cacheFileResult.hashArch         = "x86_64";
+        cacheFileResult.hashType         = "sha256";
+        cacheFileResult.hash             = "";
+
+        builder->fileResultStorage_v1.emplace_back(cacheFileResult);
+        builder->fileResults.push_back((FileResult*)&builder->fileResultStorage_v1.back());
+
+        builder->buffersToFree.emplace_back((FileBuffer) { (void*)buffer, bufferSize });
+
+        success = true;
+    });
+    return success;
+}
+
+
 bool runSharedCacheBuilder(struct MRMSharedCacheBuilder* builder) {
+    if ( builder->options->disposition == SymbolsCache )
+        return runSymbolsCacheBuilder(builder);
+
     __block bool success = false;
     builder->runSync(^() {
         if ( !createBuilders(builder) )
@@ -925,13 +1285,8 @@
         calculateDylibsToDelete(builder);
 
         // Copy from the storage to the vector we can return to the API.
-#if SUPPORT_CACHE_BUILDER_MEMORY_BUFFERS
         for (auto &fileResult : builder->fileResultStorage_v1)
             builder->fileResults.push_back((FileResult*)&fileResult);
-#else
-        for (auto &fileResult : builder->fileResultStorage_v2)
-            builder->fileResults.push_back((FileResult*)&fileResult);
-#endif
         for (auto &cacheResult : builder->cacheResultStorage)
             builder->cacheResults.push_back(&cacheResult);
 
@@ -975,6 +1330,15 @@
     return builder->filesToRemove.data();
 }
 
+const char* const* getCacheStats(const struct MRMSharedCacheBuilder* builder, uint64_t* resultCount) {
+    if ( builder->stats.empty() ) {
+        *resultCount = 0;
+        return nullptr;
+    }
+    *resultCount = builder->stats.size();
+    return builder->stats.data();
+}
+
 void destroySharedCacheBuilder(struct MRMSharedCacheBuilder* builder) {
 
     for ( FileBuffer& buffer : builder->buffersToFree ) {
@@ -985,16 +1349,15 @@
         vm_deallocate(mach_task_self(), (vm_address_t)buffer.buffer, buffer.size);
     }
 
-#if !SUPPORT_CACHE_BUILDER_MEMORY_BUFFERS
-    for ( MappedBuffer& buffer : builder->buffersToUnmap ) {
-        ::munmap(buffer.buffer, buffer.size);
-        ::close(buffer.fd);
-
-        // The builder tool will link this temp path to the new location, if needed.  We then
-        // remove the old path with this unlink().
-        ::unlink(buffer.tempPath.c_str());
-    }
-#endif
-
     delete builder;
 }
+
+
+// rdar://146678211 (slc_builder.dylib is using libcpp_verbose_abort() which does not exist on builder)
+// This can be removed when IA builders update to a host whose libc++.dylib has the symbol __ZNSt3__122__libcpp_verbose_abortEPKcz
+__attribute__((__noreturn__, visibility("hidden")))
+void _libcpp_verbose_abort(const char* msg, ...) __asm("__ZNSt3__122__libcpp_verbose_abortEPKcz");
+void _libcpp_verbose_abort(const char* msg, ...)
+{
+    abort();
+}