Loading...
cache_builder/SubCache.cpp dyld-1340 dyld-1125.5
--- dyld/dyld-1340/cache_builder/SubCache.cpp
+++ dyld/dyld-1125.5/cache_builder/SubCache.cpp
@@ -25,10 +25,8 @@
 #include "BuilderConfig.h"
 #include "BuilderOptions.h"
 #include "CacheDylib.h"
-#include "Chunk.h"
 #include "CodeSigningTypes.h"
 #include "DyldSharedCache.h"
-#include "Header.h"
 #include "SubCache.h"
 #include "JSONWriter.h"
 
@@ -38,10 +36,8 @@
 #include <CommonCrypto/CommonDigest.h>
 #include <CommonCrypto/CommonDigestSPI.h>
 
+using dyld3::GradedArchs;
 using dyld3::MachOFile;
-
-using mach_o::Header;
-using mach_o::Platform;
 
 using error::Error;
 
@@ -58,59 +54,63 @@
 // MARK: --- SharedCacheBuilder::Region methods ---
 
 uint32_t Region::initProt() const
-{
-    uint32_t initProt = 0;
-    switch ( this->kind ) {
-        case Region::Kind::text:
-            initProt = VM_PROT_READ | VM_PROT_EXECUTE;
-            break;
-        case Region::Kind::data:
-        case Region::Kind::auth:
-            initProt = VM_PROT_READ | VM_PROT_WRITE;
-            break;
-        case Region::Kind::dataConst:
-        case Region::Kind::authConst:
-        case Region::Kind::tproConst:
-        case Region::Kind::tproAuthConst:
-            initProt = VM_PROT_READ;
-            break;
-        case Region::Kind::readOnly:
-        case Region::Kind::linkedit:
-            initProt = VM_PROT_READ;
-            break;
-        case Region::Kind::dynamicConfig:
-            // HACK: This is not actually mapped, but it is used in vm calculations
-            initProt = VM_PROT_READ;
-            break;
-        case Region::Kind::unmapped:
-        case Region::Kind::codeSignature:
-            // This isn't mapped, so we should never ask for its initProt
-            assert(0);
-            break;
-        case Region::Kind::numKinds:
-            assert(0);
-            break;
-    }
-
-    return initProt;
-}
-
-uint32_t Region::maxProt() const
 {
     uint32_t maxProt = 0;
     switch ( this->kind ) {
         case Region::Kind::text:
             maxProt = VM_PROT_READ | VM_PROT_EXECUTE;
             break;
-        case Region::Kind::tproConst:
         case Region::Kind::data:
+            maxProt = VM_PROT_READ | VM_PROT_WRITE;
+            break;
         case Region::Kind::dataConst:
-        case Region::Kind::tproAuthConst:
+            maxProt = VM_PROT_READ;
+            break;
         case Region::Kind::auth:
+            maxProt = VM_PROT_READ | VM_PROT_WRITE;
+            break;
+        case Region::Kind::authConst:
+            maxProt = VM_PROT_READ;
+            break;
+        case Region::Kind::linkedit:
+            maxProt = VM_PROT_READ;
+            break;
+        case Region::Kind::dynamicConfig:
+            // HACK: This is not actually mapped, but it is used in vm calculations
+            maxProt = VM_PROT_READ;
+            break;
+        case Region::Kind::unmapped:
+        case Region::Kind::codeSignature:
+            // This isn't mapped, so we should never ask for its maxprot
+            assert(0);
+            break;
+        case Region::Kind::numKinds:
+            assert(0);
+            break;
+    }
+
+    return maxProt;
+}
+
+uint32_t Region::maxProt() const
+{
+    uint32_t maxProt = 0;
+    switch ( this->kind ) {
+        case Region::Kind::text:
+            maxProt = VM_PROT_READ | VM_PROT_EXECUTE;
+            break;
+        case Region::Kind::data:
+            maxProt = VM_PROT_READ | VM_PROT_WRITE;
+            break;
+        case Region::Kind::dataConst:
+            maxProt = VM_PROT_READ | VM_PROT_WRITE;
+            break;
+        case Region::Kind::auth:
+            maxProt = VM_PROT_READ | VM_PROT_WRITE;
+            break;
         case Region::Kind::authConst:
             maxProt = VM_PROT_READ | VM_PROT_WRITE;
             break;
-        case Region::Kind::readOnly:
         case Region::Kind::linkedit:
             maxProt = VM_PROT_READ;
             break;
@@ -139,19 +139,14 @@
             // We should never ask this region if it has auth content
             assert(0);
             break;
-        case Region::Kind::tproConst:
-        case Region::Kind::tproAuthConst:
         case Region::Kind::data:
         case Region::Kind::dataConst:
-            // Note TPRO never contains authenticated fixups, so its name is wrong
-            // but its really there to be placed next to the AUTH regions
             result = false;
             break;
         case Region::Kind::auth:
         case Region::Kind::authConst:
             result = true;
             break;
-        case Region::Kind::readOnly:
         case Region::Kind::linkedit:
         case Region::Kind::dynamicConfig:
             // We should never ask this region if it has auth content
@@ -175,13 +170,10 @@
 {
     switch ( this->kind ) {
         case Region::Kind::text:
-        case Region::Kind::tproConst:
         case Region::Kind::data:
         case Region::Kind::dataConst:
-        case Region::Kind::tproAuthConst:
         case Region::Kind::auth:
         case Region::Kind::authConst:
-        case Region::Kind::readOnly:
         case Region::Kind::linkedit:
             return true;
         case Region::Kind::unmapped:
@@ -198,13 +190,10 @@
 {
     switch ( this->kind ) {
         case Region::Kind::text:
-        case Region::Kind::tproConst:
         case Region::Kind::data:
         case Region::Kind::dataConst:
-        case Region::Kind::tproAuthConst:
         case Region::Kind::auth:
         case Region::Kind::authConst:
-        case Region::Kind::readOnly:
         case Region::Kind::linkedit:
         case Region::Kind::dynamicConfig:
             return true;
@@ -232,16 +221,10 @@
             bool nextIsRW = (next.initProt() & VM_PROT_WRITE) != 0;
             return nextIsRW;
         }
-        case Region::Kind::tproConst:
-        case Region::Kind::tproAuthConst:
         case Region::Kind::data:
         case Region::Kind::auth: {
             // HACK: Remove once we have rdar://96315050
             if ( (this->kind == Region::Kind::auth) && (next.kind == Region::Kind::authConst) )
-                return false;
-
-            // Don't add adding from data to tproConst as we didn't have padding from data to auth before
-            if ( (next.kind == Kind::tproConst) || (next.kind == Kind::tproAuthConst) )
                 return false;
 
             // Add padding if DATA is adjacent to something immutable, eg TEXT/DATA_CONST
@@ -250,17 +233,12 @@
         }
         case Region::Kind::dataConst:
         case Region::Kind::authConst: {
-            // Always add padding from DATA_CONST to TPRO_CONST as TPRO_CONST will actually be dirtied
-            if ( (next.kind == Kind::tproConst) || (next.kind == Kind::tproAuthConst) )
-                return true;
-
             // Don't add padding if *_CONST is next to *_CONST, otherwise add padding
             bool nextInitIsRO = (next.initProt() & VM_PROT_WRITE) == 0;
             bool nextMaxIsRW = (next.maxProt() & VM_PROT_WRITE) != 0;
             bool nextIsDataConst = nextInitIsRO & nextMaxIsRW;
             return !nextIsDataConst;
         }
-        case Region::Kind::readOnly:
         case Region::Kind::linkedit:
         case Region::Kind::dynamicConfig: {
             // Add padding if LINKEDIT is adjacent to something that is mutable,
@@ -343,40 +321,10 @@
         switch ( region.kind ) {
             case cache_builder::Region::Kind::text:
                 break;
-            case cache_builder::Region::Kind::tproConst:
-            case cache_builder::Region::Kind::tproAuthConst:
             case cache_builder::Region::Kind::dataConst:
             case cache_builder::Region::Kind::data:
             case cache_builder::Region::Kind::auth:
             case cache_builder::Region::Kind::authConst:
-                return true;
-            case Region::Kind::readOnly:
-            case cache_builder::Region::Kind::linkedit:
-            case cache_builder::Region::Kind::unmapped:
-            case cache_builder::Region::Kind::dynamicConfig:
-            case cache_builder::Region::Kind::codeSignature:
-            case cache_builder::Region::Kind::numKinds:
-                break;
-        }
-    }
-    return false;
-}
-
-static bool hasReadOnlyRegion(std::span<Region> regions)
-{
-    for ( const Region& region : regions ) {
-        if ( region.chunks.empty() )
-            continue;
-        switch ( region.kind ) {
-            case cache_builder::Region::Kind::text:
-            case cache_builder::Region::Kind::tproConst:
-            case cache_builder::Region::Kind::tproAuthConst:
-            case cache_builder::Region::Kind::dataConst:
-            case cache_builder::Region::Kind::data:
-            case cache_builder::Region::Kind::auth:
-            case cache_builder::Region::Kind::authConst:
-                break;
-            case Region::Kind::readOnly:
                 return true;
             case cache_builder::Region::Kind::linkedit:
             case cache_builder::Region::Kind::unmapped:
@@ -396,13 +344,10 @@
             continue;
         switch ( region.kind ) {
             case cache_builder::Region::Kind::text:
-            case cache_builder::Region::Kind::tproConst:
-            case cache_builder::Region::Kind::tproAuthConst:
             case cache_builder::Region::Kind::dataConst:
             case cache_builder::Region::Kind::data:
             case cache_builder::Region::Kind::auth:
             case cache_builder::Region::Kind::authConst:
-            case Region::Kind::readOnly:
                 break;
             case cache_builder::Region::Kind::linkedit:
                 return true;
@@ -416,46 +361,43 @@
     return false;
 }
 
-void SubCache::setSuffix(Platform platform, bool forceDevelopmentSubCacheSuffix, size_t subCacheIndex)
+void SubCache::setSuffix(dyld3::Platform platform, bool forceDevelopmentSubCacheSuffix,
+                         size_t subCacheIndex)
 {
     assert(this->isSubCache() || this->isStubsCache());
     assert(subCacheIndex > 0);
 
     const char* dataSuffix = forceDevelopmentSubCacheSuffix ? ".development.dylddata" : ".dylddata";
     const char* linkeditSuffix = forceDevelopmentSubCacheSuffix ? ".development.dyldlinkedit" : ".dyldlinkedit";
-    const char* readonlySuffix = forceDevelopmentSubCacheSuffix ? ".development.dyldreadonly" : ".dyldreadonly";
     const char* subCacheSuffix = forceDevelopmentSubCacheSuffix ? ".development" : "";
 
-    if ( (platform == Platform::macOS) || platform.isSimulator() ) {
-        // macOS/sims never has a .development suffix
-        this->fileSuffix = "." + json::decimal(subCacheIndex);
-    } else if ( platform == Platform::driverKit ) {
+    if ( platform == dyld3::Platform::macOS ) {
+        // macOS never has a .development suffix
+        this->fileSuffix = "." + dyld3::json::decimal(subCacheIndex);
+    } else if ( platform == dyld3::Platform::driverKit ) {
         // driverKit never has a .development suffix
-        this->fileSuffix = "." + json::decimal(subCacheIndex);
+        this->fileSuffix = "." + dyld3::json::decimal(subCacheIndex);
     } else if ( this->isStubsDevelopmentCache() ) {
         // Dev stubs always have a suffix
-        this->fileSuffix = "." + json::decimal(subCacheIndex) + ".development";
+        this->fileSuffix = "." + dyld3::json::decimal(subCacheIndex) + ".development";
     } else if ( this->isStubsCustomerCache() ) {
         // Customer stubs never have a suffix
-        this->fileSuffix = "." + json::decimal(subCacheIndex);
+        this->fileSuffix = "." + dyld3::json::decimal(subCacheIndex);
     } else if ( hasDataRegion(this->regions) ) {
         // Data only subcaches have their own suffix
-        this->fileSuffix = "." + json::decimal(subCacheIndex) + dataSuffix;
-    } else if ( hasReadOnlyRegion(this->regions) ) {
-        // read-only only subcaches have their own suffix
-        this->fileSuffix = "." + json::decimal(subCacheIndex) + readonlySuffix;
+        this->fileSuffix = "." + dyld3::json::decimal(subCacheIndex) + dataSuffix;
     } else if ( hasLinkeditRegion(this->regions) ) {
         // Linkedit only subcaches have their own suffix
-        this->fileSuffix = "." + json::decimal(subCacheIndex) + linkeditSuffix;
+        this->fileSuffix = "." + dyld3::json::decimal(subCacheIndex) + linkeditSuffix;
     } else {
-        this->fileSuffix = "." + json::decimal(subCacheIndex) + subCacheSuffix;
+        this->fileSuffix = "." + dyld3::json::decimal(subCacheIndex) + subCacheSuffix;
     }
 }
 
 static std::string getCodeSigningIdentifier(const BuilderOptions& options)
 {
     std::string cacheIdentifier = "com.apple.dyld.cache.";
-    cacheIdentifier += options.arch.name();
+    cacheIdentifier += options.archs.name();
     if ( options.dylibsRemovedFromDisk ) {
         switch ( options.kind ) {
             case CacheKind::development:
@@ -743,14 +685,6 @@
     // Note: cdHash is defined as first 20 bytes of hash
     memcpy(this->cdHash, fullCdHash, 20);
 
-    if ( layout.agile ) {
-        // hash of entire code directory (cdHash) uses same hash as each page
-        uint8_t altfullCdHash[CS_HASH_SIZE_SHA256];
-        CCDigest(kCCDigestSHA256, (const uint8_t*)cd256, layout.cd256Size, altfullCdHash);
-        // Note: cdHash is defined as first 20 bytes of hash
-        memcpy(this->agilecdHash, altfullCdHash, 20);
-    }
-
     // Set the UUID string in the subcache
     uuid_unparse_upper(dyldCacheHeader->uuid, this->uuidString);
 }
@@ -766,20 +700,6 @@
     this->regions[(uint32_t)Region::Kind::text].chunks.push_back(chunk);
 }
 
-void SubCache::addTPROConstChunk(const BuilderConfig& config, Chunk* chunk)
-{
-    // Rosetta can't handle an extra shared cache mapping, so use DATA instead
-    // We use DATA instead of DATA_CONST because we don't want the MemoryManager
-    // and DataConstWriter to both mprotect the same pages.  But the DataConstWriter
-    // will ignore everything in DATA while the MemoryManager will find TPRO there
-    if ( config.layout.discontiguous.has_value() )
-        this->addDataChunk(chunk);
-    else if ( config.layout.hasAuthRegion )
-        this->regions[(uint32_t)Region::Kind::tproAuthConst].chunks.push_back(chunk);
-    else
-        this->regions[(uint32_t)Region::Kind::tproConst].chunks.push_back(chunk);
-}
-
 void SubCache::addDataChunk(Chunk* chunk)
 {
     this->regions[(uint32_t)Region::Kind::data].chunks.push_back(chunk);
@@ -800,15 +720,6 @@
     this->regions[(uint32_t)Region::Kind::authConst].chunks.push_back(chunk);
 }
 
-void SubCache::addReadOnlyChunk(const BuilderConfig& config, Chunk* chunk)
-{
-    // Rosetta can't handle an extra shared cache mapping, so use __TEXT instead
-    if ( config.layout.discontiguous.has_value() )
-        this->addTextChunk(chunk);
-    else
-        this->regions[(uint32_t)Region::Kind::readOnly].chunks.push_back(chunk);
-}
-
 void SubCache::addLinkeditChunk(Chunk* chunk)
 {
     this->regions[(uint32_t)Region::Kind::linkedit].chunks.push_back(chunk);
@@ -822,6 +733,22 @@
 void SubCache::addCodeSignatureChunk(Chunk* chunk)
 {
     this->regions[(uint32_t)Region::Kind::codeSignature].chunks.push_back(chunk);
+}
+
+// HACK: We need to insert the libobjc __TEXT first so that its before all the other OBJC_RO chunks
+void SubCache::addObjCTextChunk(Chunk* chunk)
+{
+    std::vector<Chunk*>& chunks = this->regions[(uint32_t)Region::Kind::text].chunks;
+    chunks.insert(chunks.begin(), chunk);
+}
+
+// ObjC optimizations need to add read-only chunks.  For now these are added to the start
+// of TEXT, so that they are in the same subCache when split by One Cache.  In future we want to
+// move these to LINKEDIT
+void SubCache::addObjCReadOnlyChunk(Chunk* chunk)
+{
+    std::vector<Chunk*>& chunks = this->regions[(uint32_t)Region::Kind::text].chunks;
+    chunks.insert(chunks.begin(), chunk);
 }
 
 // All objc optimizations need to be contiguous, but that means if any need AUTH then all do
@@ -835,17 +762,18 @@
     }
 }
 
-void SubCache::addDylib(const BuilderConfig& config, CacheDylib& cacheDylib)
+void SubCache::addDylib(CacheDylib& cacheDylib, bool addLinkedit)
 {
     for ( DylibSegmentChunk& segmentInfo : cacheDylib.segments ) {
         switch ( segmentInfo.kind ) {
             case Chunk::Kind::dylibText:
-                this->addTextChunk(&segmentInfo);
-                break;
-            case Chunk::Kind::tproDataConst:
-                this->addTPROConstChunk(config, &segmentInfo);
+                if ( cacheDylib.installName == "/usr/lib/libobjc.A.dylib" )
+                    this->addObjCTextChunk(&segmentInfo);
+                else
+                    this->addTextChunk(&segmentInfo);
                 break;
             case Chunk::Kind::dylibData:
+            case Chunk::Kind::dylibDataConstWorkaround:
                 this->addDataChunk(&segmentInfo);
                 break;
             case Chunk::Kind::dylibDataConst:
@@ -853,19 +781,21 @@
                 break;
             case Chunk::Kind::dylibDataDirty:
                 // On arm64e, dataDirty goes in to auth
-                if ( cacheDylib.inputHdr->isArch("arm64e") )
+                if ( cacheDylib.inputMF->isArch("arm64e") )
                     this->addAuthChunk(&segmentInfo);
                 else
                     this->addDataChunk(&segmentInfo);
                 break;
             case Chunk::Kind::dylibAuth:
+            case Chunk::Kind::dylibAuthConstWorkaround:
                 this->addAuthChunk(&segmentInfo);
                 break;
             case Chunk::Kind::dylibAuthConst:
                 this->addAuthConstChunk(&segmentInfo);
                 break;
             case Chunk::Kind::dylibReadOnly:
-                this->addReadOnlyChunk(config, &segmentInfo);
+                // FIXME: Read-only data should really be in a read-only mapping.
+                this->addTextChunk(&segmentInfo);
                 break;
             case Chunk::Kind::dylibLinkedit:
                 // Skip adding here.  We'll do this in addLinkeditFromDylib()
@@ -875,6 +805,9 @@
                 break;
         }
     }
+
+    if ( addLinkedit )
+        this->addLinkeditFromDylib(cacheDylib);
 }
 
 // Linkedit is stored in Chunks in its own array on the dylib.  This adds it to the subCache.
@@ -888,102 +821,19 @@
         this->addLinkeditChunk(&chunk);
 }
 
-void SubCache::forEachTPRORegionInData(SubCache* mainSubCache, std::span<SubCache*> subCaches,
-                                       void (^callback)(Region& region, const Chunk* firstChunk, const Chunk* lastChunk))
-{
-    // Find all subcaches with TPRO regions, or on x86_64 its those with DATA in them
-    auto runOnSubCache = ^(SubCache* subCache) {
-        for ( Region& region : subCache->regions ) {
-            if ( region.kind != Region::Kind::data )
-                continue;
-
-            const Chunk* firstTPROChunk = nullptr;
-            const Chunk* lastTPROChunk = nullptr;
-            for ( const Chunk* chunk : region.chunks ) {
-                if ( chunk->isTPROChunk() ) {
-                    if ( !firstTPROChunk ) {
-                        firstTPROChunk = chunk;
-                        lastTPROChunk = chunk;
-                    } else {
-                        lastTPROChunk = chunk;
-                    }
-                } else if ( firstTPROChunk ) {
-                    // HACK: If we find an alignment chunk then add it as-if its a TPRO chunk. This is to
-                    // make sure callers can get page-aligned TPRO regions
-                    if ( const AlignChunk* alignChunk = chunk->isAlignChunk() ) {
-                        if ( alignChunk->alignment() >= 4_KB ) {
-                            lastTPROChunk = chunk;
-                            continue;
-                        }
-                    }
-
-                    // Found a non-tpro chunk.  Break here and do the callback outside the chunk loop
-                    // We don't need to continue the loop as we sorted all the TPRO to be adjacent
-                    break;
-                }
-            }
-
-            if ( firstTPROChunk ) {
-                // Ended with a tpro chunk.  Make the callback for it
-                callback(region, firstTPROChunk, lastTPROChunk);
-            }
-        }
-    };
-
-    runOnSubCache(mainSubCache);
-    for ( SubCache* subCache : subCaches )
-        runOnSubCache(subCache);
-}
-
-static void forEachTPRORegion(const BuilderConfig& config, SubCache* mainSubCache,
-                              std::span<SubCache*> subCaches,
-                              void (^callback)(CacheVMAddress firstChunkAddress, CacheVMAddress lastChunkAddress, CacheVMSize lastChunkSize))
-{
-    // Find all subcaches with TPRO regions, or on x86_64 its those with DATA in them
-    if ( config.layout.tproIsInData ) {
-        SubCache::forEachTPRORegionInData(mainSubCache, subCaches, ^(Region& region, const Chunk *firstChunk, const Chunk *lastChunk) {
-            callback(firstChunk->cacheVMAddress, lastChunk->cacheVMAddress, lastChunk->cacheVMSize);
-        });
-        return;
-    }
-
-    auto runOnSubCache = ^(const SubCache* subCache) {
-        for ( const Region& region : subCache->regions ) {
-            if ( (region.kind == Region::Kind::tproConst) || (region.kind == Region::Kind::tproAuthConst) )
-                callback(region.subCacheVMAddress, region.subCacheVMAddress, region.subCacheVMSize);
-        }
-    };
-
-    runOnSubCache(mainSubCache);
-    for ( const SubCache* subCache : subCaches )
-        runOnSubCache(subCache);
-}
-
-static uint32_t numTPRORegions(const BuilderConfig& config, SubCache* mainSubCache,
-                               std::span<SubCache*> subCaches)
-{
-    __block uint32_t count = 0;
-    forEachTPRORegion(config, mainSubCache, subCaches, ^(CacheVMAddress, CacheVMAddress, CacheVMSize) {
-        ++count;
-    });
-    return count;
-}
-
-void SubCache::addCacheHeaderChunk(const BuilderOptions& options, const BuilderConfig& config,
-                                   const std::span<CacheDylib> cacheDylibs)
+void SubCache::addCacheHeaderChunk(const std::span<CacheDylib> cacheDylibs)
 {
     // calculate size of header info and where first dylib's mach_header should start
     uint64_t numMappings = this->regions.size();
     uint64_t startOffset = sizeof(dyld_cache_header) + (numMappings * sizeof(dyld_cache_mapping_info));
     startOffset += numMappings * sizeof(dyld_cache_mapping_and_slide_info);
 
-    // Only the main cache has a list of subCaches and TPRO mappings to write
+    // Only the main cache has a list of subCaches to write
     if ( this->isMainCache() ) {
         startOffset += sizeof(dyld_subcache_entry) * this->subCaches.size();
-        startOffset += sizeof(dyld_cache_tpro_mapping_info) * numTPRORegions(config, this, this->subCaches);
-    }
-
-    if ( this->needsCacheHeaderImageList(options) ) {
+    }
+
+    if ( this->needsCacheHeaderImageList() ) {
         startOffset += sizeof(dyld_cache_image_info) * cacheDylibs.size();
         startOffset += sizeof(dyld_cache_image_text_info) * cacheDylibs.size();
         for ( const CacheDylib& cacheDylib : cacheDylibs ) {
@@ -1019,12 +869,6 @@
     // we sort the Chunk's.  For now just add placeholder(s) and we'll update the
     // size in calculateSlideInfoSize()
 
-    // TPRO_CONST
-    if ( !this->regions[(uint32_t)Region::Kind::tproConst].chunks.empty() ) {
-        this->tproConstSlideInfo = std::make_unique<SlideInfoChunk>();
-        addLinkeditChunk(this->tproConstSlideInfo.get());
-    }
-
     // DATA
     if ( !this->regions[(uint32_t)Region::Kind::data].chunks.empty() ) {
         this->dataSlideInfo = std::make_unique<SlideInfoChunk>();
@@ -1037,12 +881,6 @@
         addLinkeditChunk(this->dataConstSlideInfo.get());
     }
 
-    // TPRO_CONST (on arm64e)
-    if ( !this->regions[(uint32_t)Region::Kind::tproAuthConst].chunks.empty() ) {
-        this->tproAuthConstSlideInfo = std::make_unique<SlideInfoChunk>();
-        addLinkeditChunk(this->tproAuthConstSlideInfo.get());
-    }
-
     // AUTH
     if ( !this->regions[(uint32_t)Region::Kind::auth].chunks.empty() ) {
         this->authSlideInfo = std::make_unique<SlideInfoChunk>();
@@ -1065,7 +903,7 @@
     addCodeSignatureChunk(this->codeSignature.get());
 }
 
-void SubCache::addObjCOptsHeaderChunk(const BuilderConfig& config, ObjCOptimizer& objcOptimizer)
+void SubCache::addObjCOptsHeaderChunk(ObjCOptimizer& objcOptimizer)
 {
     this->objcOptsHeader = std::make_unique<ObjCOptsHeaderChunk>();
     this->objcOptsHeader->cacheVMSize       = CacheVMSize(objcOptimizer.optsHeaderByteSize);
@@ -1073,10 +911,10 @@
 
     objcOptimizer.optsHeaderChunk = this->objcOptsHeader.get();
 
-    this->addReadOnlyChunk(config, this->objcOptsHeader.get());
-}
-
-void SubCache::addObjCHeaderInfoReadOnlyChunk(const BuilderConfig& config, ObjCOptimizer& objcOptimizer)
+    this->addLinkeditChunk(this->objcOptsHeader.get());
+}
+
+void SubCache::addObjCHeaderInfoReadOnlyChunk(ObjCOptimizer& objcOptimizer)
 {
     this->objcHeaderInfoRO = std::make_unique<ObjCHeaderInfoReadOnlyChunk>();
     this->objcHeaderInfoRO->cacheVMSize         = CacheVMSize(objcOptimizer.headerInfoReadOnlyByteSize);
@@ -1084,10 +922,10 @@
 
     objcOptimizer.headerInfoReadOnlyChunk = this->objcHeaderInfoRO.get();
 
-    this->addReadOnlyChunk(config, this->objcHeaderInfoRO.get());
-}
-
-void SubCache::addObjCImageInfoChunk(const BuilderConfig& config, ObjCOptimizer& objcOptimizer)
+    this->addObjCReadOnlyChunk(this->objcHeaderInfoRO.get());
+}
+
+void SubCache::addObjCImageInfoChunk(ObjCOptimizer& objcOptimizer)
 {
     this->objcImageInfo = std::make_unique<ObjCImageInfoChunk>();
     this->objcImageInfo->cacheVMSize         = CacheVMSize(objcOptimizer.imageInfoSize);
@@ -1095,10 +933,10 @@
 
     objcOptimizer.imageInfoChunk = this->objcImageInfo.get();
 
-    this->addReadOnlyChunk(config, this->objcImageInfo.get());
-}
-
-void SubCache::addObjCSelectorStringsChunk(const BuilderConfig& config, ObjCSelectorOptimizer& objCSelectorOptimizer)
+    this->addObjCReadOnlyChunk(this->objcImageInfo.get());
+}
+
+void SubCache::addObjCSelectorStringsChunk(ObjCSelectorOptimizer& objCSelectorOptimizer)
 {
     this->objcSelectorStrings = std::make_unique<ObjCStringsChunk>();
     this->objcSelectorStrings->cacheVMSize      = CacheVMSize(objCSelectorOptimizer.selectorStringsTotalByteSize);
@@ -1106,10 +944,10 @@
 
     objCSelectorOptimizer.selectorStringsChunk = this->objcSelectorStrings.get();
 
-    this->addReadOnlyChunk(config, this->objcSelectorStrings.get());
-}
-
-void SubCache::addObjCSelectorHashTableChunk(const BuilderConfig& config, ObjCSelectorOptimizer& objCSelectorOptimizer)
+    this->addObjCReadOnlyChunk(this->objcSelectorStrings.get());
+}
+
+void SubCache::addObjCSelectorHashTableChunk(ObjCSelectorOptimizer& objCSelectorOptimizer)
 {
     this->objcSelectorsHashTable = std::make_unique<ObjCSelectorHashTableChunk>();
     this->objcSelectorsHashTable->cacheVMSize       = CacheVMSize(objCSelectorOptimizer.selectorHashTableTotalByteSize);
@@ -1117,10 +955,10 @@
 
     objCSelectorOptimizer.selectorHashTableChunk = this->objcSelectorsHashTable.get();
 
-    this->addReadOnlyChunk(config, this->objcSelectorsHashTable.get());
-}
-
-void SubCache::addObjCClassNameStringsChunk(const BuilderConfig& config, ObjCClassOptimizer& objcClassOptimizer)
+    this->addObjCReadOnlyChunk(this->objcSelectorsHashTable.get());
+}
+
+void SubCache::addObjCClassNameStringsChunk(ObjCClassOptimizer& objcClassOptimizer)
 {
     this->objcClassNameStrings = std::make_unique<ObjCStringsChunk>();
     this->objcClassNameStrings->cacheVMSize         = CacheVMSize(objcClassOptimizer.nameStringsTotalByteSize);
@@ -1128,10 +966,10 @@
 
     objcClassOptimizer.classNameStringsChunk = this->objcClassNameStrings.get();
 
-    this->addReadOnlyChunk(config, this->objcClassNameStrings.get());
-}
-
-void SubCache::addObjCClassHashTableChunk(const BuilderConfig& config, ObjCClassOptimizer& objcClassOptimizer)
+    this->addObjCReadOnlyChunk(this->objcClassNameStrings.get());
+}
+
+void SubCache::addObjCClassHashTableChunk(ObjCClassOptimizer& objcClassOptimizer)
 {
     this->objcClassesHashTable = std::make_unique<ObjCClassHashTableChunk>();
     this->objcClassesHashTable->cacheVMSize         = CacheVMSize(objcClassOptimizer.classHashTableTotalByteSize);
@@ -1139,10 +977,10 @@
 
     objcClassOptimizer.classHashTableChunk = this->objcClassesHashTable.get();
 
-    this->addReadOnlyChunk(config, this->objcClassesHashTable.get());
-}
-
-void SubCache::addObjCProtocolNameStringsChunk(const BuilderConfig& config, ObjCProtocolOptimizer& objcProtocolOptimizer)
+    this->addObjCReadOnlyChunk(this->objcClassesHashTable.get());
+}
+
+void SubCache::addObjCProtocolNameStringsChunk(ObjCProtocolOptimizer& objcProtocolOptimizer)
 {
     this->objcProtocolNameStrings = std::make_unique<ObjCStringsChunk>();
     this->objcProtocolNameStrings->cacheVMSize      = CacheVMSize(objcProtocolOptimizer.nameStringsTotalByteSize);
@@ -1150,10 +988,10 @@
 
     objcProtocolOptimizer.protocolNameStringsChunk = this->objcProtocolNameStrings.get();
 
-    this->addReadOnlyChunk(config, this->objcProtocolNameStrings.get());
-}
-
-void SubCache::addObjCProtocolHashTableChunk(const BuilderConfig& config, ObjCProtocolOptimizer& objcProtocolOptimizer)
+    this->addObjCReadOnlyChunk(this->objcProtocolNameStrings.get());
+}
+
+void SubCache::addObjCProtocolHashTableChunk(ObjCProtocolOptimizer& objcProtocolOptimizer)
 {
     this->objcProtocolsHashTable = std::make_unique<ObjCProtocolHashTableChunk>();
     this->objcProtocolsHashTable->cacheVMSize       = CacheVMSize(objcProtocolOptimizer.protocolHashTableTotalByteSize);
@@ -1161,10 +999,10 @@
 
     objcProtocolOptimizer.protocolHashTableChunk = this->objcProtocolsHashTable.get();
 
-    this->addReadOnlyChunk(config, this->objcProtocolsHashTable.get());
-}
-
-void SubCache::addObjCProtocolSwiftDemangledNamesChunk(const BuilderConfig& config, ObjCProtocolOptimizer& objcProtocolOptimizer)
+    this->addObjCReadOnlyChunk(this->objcProtocolsHashTable.get());
+}
+
+void SubCache::addObjCProtocolSwiftDemangledNamesChunk(ObjCProtocolOptimizer& objcProtocolOptimizer)
 {
     this->objcSwiftDemangledNameStrings = std::make_unique<ObjCStringsChunk>();
     this->objcSwiftDemangledNameStrings->cacheVMSize        = CacheVMSize(objcProtocolOptimizer.swiftDemangledNameStringsTotalByteSize);
@@ -1172,7 +1010,7 @@
 
     objcProtocolOptimizer.swiftDemangledNameStringsChunk = this->objcSwiftDemangledNameStrings.get();
 
-    this->addReadOnlyChunk(config, this->objcSwiftDemangledNameStrings.get());
+    this->addObjCReadOnlyChunk(this->objcSwiftDemangledNameStrings.get());
 }
 
 void SubCache::addObjCCanonicalProtocolsChunk(const BuilderConfig& config,
@@ -1188,7 +1026,7 @@
     addObjCReadWriteChunk(config, this->objcCanonicalProtocols.get());
 }
 
-void SubCache::addObjCIMPCachesChunk(const BuilderConfig& config, ObjCIMPCachesOptimizer& objcIMPCachesOptimizer)
+void SubCache::addObjCIMPCachesChunk(ObjCIMPCachesOptimizer& objcIMPCachesOptimizer)
 {
     this->objcIMPCaches = std::make_unique<ObjCIMPCachesChunk>();
     this->objcIMPCaches->cacheVMSize                        = CacheVMSize(objcIMPCachesOptimizer.impCachesTotalByteSize);
@@ -1196,7 +1034,7 @@
 
     objcIMPCachesOptimizer.impCachesChunk = this->objcIMPCaches.get();
 
-    this->addReadOnlyChunk(config, this->objcIMPCaches.get());
+    this->addLinkeditChunk(this->objcIMPCaches.get());
 }
 
 void SubCache::addObjCCategoriesChunk(const BuilderConfig& config,
@@ -1209,7 +1047,7 @@
     objcCategoryOptimizer.categoriesChunk = this->objcCategories.get();
 
     // Add objc categories
-    addReadOnlyChunk(config, this->objcCategories.get());
+    addObjCReadOnlyChunk(this->objcCategories.get());
 }
 
 void SubCache::addCacheTrieChunk(DylibTrieOptimizer& dylibTrieOptimizer)
@@ -1225,26 +1063,16 @@
 
 void SubCache::addPatchTableChunk(PatchTableOptimizer& patchTableOptimizer)
 {
-    // We can't compute the size yet so just make an empty chunk
+    // We can't compute the size yet.  We need to know how many fixups we have
+    // And yet we have an estimate, so we'll use it
 
     this->patchTable = std::make_unique<PatchTableChunk>();
-    this->patchTable->cacheVMSize       = CacheVMSize(0ULL);
-    this->patchTable->subCacheFileSize  = CacheFileSize(0ULL);
+    this->patchTable->cacheVMSize       = CacheVMSize(patchTableOptimizer.patchTableTotalByteSize);
+    this->patchTable->subCacheFileSize  = CacheFileSize(patchTableOptimizer.patchTableTotalByteSize);
 
     patchTableOptimizer.patchTableChunk = this->patchTable.get();
 
     this->addLinkeditChunk(this->patchTable.get());
-}
-
-void SubCache::addFunctionVariantsChunk(FunctionVariantsOptimizer& optimizer)
-{
-    this->functionVariants                    = std::make_unique<FunctionVariantsPatchTableChunk>();
-    this->functionVariants->cacheVMSize       = CacheVMSize(optimizer.fvInfoTotalByteSize);
-    this->functionVariants->subCacheFileSize  = CacheFileSize(optimizer.fvInfoTotalByteSize);
-
-    optimizer.chunk = this->functionVariants.get();
-
-    this->addLinkeditChunk(this->functionVariants.get());
 }
 
 void SubCache::addCacheDylibsLoaderChunk(PrebuiltLoaderBuilder& builder)
@@ -1286,21 +1114,7 @@
     this->addLinkeditChunk(this->executablesTrie.get());
 }
 
-void SubCache::addPrewarmingDataChunk(const BuilderConfig& config, PrewarmingOptimizer& opt)
-{
-    if ( opt.prewarmingByteSize == 0 )
-        return;
-
-    this->prewarmingChunk = std::make_unique<PrewarmingChunk>(Chunk::Kind::prewarmingData);
-    this->prewarmingChunk->cacheVMSize      = CacheVMSize(opt.prewarmingByteSize);
-    this->prewarmingChunk->subCacheFileSize = CacheFileSize(opt.prewarmingByteSize);
-
-    opt.prewarmingChunk = this->prewarmingChunk.get();
-
-    this->addReadOnlyChunk(config, this->prewarmingChunk.get());
-}
-
-void SubCache::addSwiftOptsHeaderChunk(const BuilderConfig& config, SwiftOptimizer& opt)
+void SubCache::addSwiftOptsHeaderChunk(SwiftProtocolConformanceOptimizer& opt)
 {
     this->swiftOptsHeader = std::make_unique<SwiftOptsHeaderChunk>();
     this->swiftOptsHeader->cacheVMSize      = CacheVMSize(opt.optsHeaderByteSize);
@@ -1308,10 +1122,10 @@
 
     opt.optsHeaderChunk = this->swiftOptsHeader.get();
 
-    this->addReadOnlyChunk(config, this->swiftOptsHeader.get());
-}
-
-void SubCache::addSwiftTypeHashTableChunk(const BuilderConfig& config, SwiftOptimizer& opt)
+    this->addLinkeditChunk(this->swiftOptsHeader.get());
+}
+
+void SubCache::addSwiftTypeHashTableChunk(SwiftProtocolConformanceOptimizer& opt)
 {
     this->swiftTypeHashTable = std::make_unique<SwiftProtocolConformancesHashTableChunk>();
     this->swiftTypeHashTable->cacheVMSize       = CacheVMSize(opt.typeConformancesHashTableSize);
@@ -1319,10 +1133,10 @@
 
     opt.typeConformancesHashTable = this->swiftTypeHashTable.get();
 
-    this->addReadOnlyChunk(config, this->swiftTypeHashTable.get());
-}
-
-void SubCache::addSwiftMetadataHashTableChunk(const BuilderConfig& config, SwiftOptimizer& opt)
+    this->addLinkeditChunk(this->swiftTypeHashTable.get());
+}
+
+void SubCache::addSwiftMetadataHashTableChunk(SwiftProtocolConformanceOptimizer& opt)
 {
     this->swiftMetadataHashTable = std::make_unique<SwiftProtocolConformancesHashTableChunk>();
     this->swiftMetadataHashTable->cacheVMSize       = CacheVMSize(opt.metadataConformancesHashTableSize);
@@ -1330,10 +1144,10 @@
 
     opt.metadataConformancesHashTable = this->swiftMetadataHashTable.get();
 
-    this->addReadOnlyChunk(config, this->swiftMetadataHashTable.get());
-}
-
-void SubCache::addSwiftForeignHashTableChunk(const BuilderConfig& config, SwiftOptimizer& opt)
+    this->addLinkeditChunk(this->swiftMetadataHashTable.get());
+}
+
+void SubCache::addSwiftForeignHashTableChunk(SwiftProtocolConformanceOptimizer& opt)
 {
     this->swiftForeignTypeHashTable = std::make_unique<SwiftProtocolConformancesHashTableChunk>();
     this->swiftForeignTypeHashTable->cacheVMSize        = CacheVMSize(opt.foreignTypeConformancesHashTableSize);
@@ -1341,19 +1155,7 @@
 
     opt.foreignTypeConformancesHashTable = this->swiftForeignTypeHashTable.get();
 
-    this->addReadOnlyChunk(config, this->swiftForeignTypeHashTable.get());
-}
-
-void SubCache::addSwiftPrespecializedMetadataPointerTableChunks(const BuilderConfig& config, SwiftOptimizer& opt)
-{
-    for ( PointerHashTableOptimizerInfo& tableInfo : opt.prespecializedMetadataHashTables ) {
-        PointerHashTableChunk* chunk = this->pointerHashTables.emplace_back(std::make_unique<PointerHashTableChunk>()).get();
-        chunk->cacheVMSize       = CacheVMSize(tableInfo.size);
-        chunk->subCacheFileSize  = CacheFileSize(tableInfo.size);
-
-        tableInfo.chunk = chunk;
-        this->addReadOnlyChunk(config, chunk);
-    }
+    this->addLinkeditChunk(this->swiftForeignTypeHashTable.get());
 }
 
 void SubCache::addUnmappedSymbols(const BuilderConfig& config, UnmappedSymbolsOptimizer& opt)
@@ -1453,26 +1255,6 @@
             case Region::Kind::text:
                 flags    = this->isStubsCache() ? DYLD_CACHE_MAPPING_TEXT_STUBS : 0;
                 break;
-            case Region::Kind::tproConst:
-                flags    = DYLD_CACHE_MAPPING_CONST_DATA | DYLD_CACHE_MAPPING_CONST_TPRO_DATA;
-
-                // Get the slide info
-                if ( this->tproConstSlideInfo ) {
-                    slideInfoFileOffset = this->tproConstSlideInfo->subCacheFileOffset;
-                    slideInfoFileSize   = this->tproConstSlideInfo->usedFileSize;
-                }
-                break;
-            case Region::Kind::tproAuthConst:
-                // Note, we don't set AUTH here.  TPRO_CONST doesn't actually contain authenticated fixups, ie, is just data
-                // but we still give it its own Region so that we can place it adjacent to dirty data
-                flags    = DYLD_CACHE_MAPPING_CONST_DATA | DYLD_CACHE_MAPPING_CONST_TPRO_DATA;
-
-                // Get the slide info
-                if ( this->tproAuthConstSlideInfo ) {
-                    slideInfoFileOffset = this->tproAuthConstSlideInfo->subCacheFileOffset;
-                    slideInfoFileSize   = this->tproAuthConstSlideInfo->usedFileSize;
-                }
-                break;
             case Region::Kind::data:
                 flags    = 0;
 
@@ -1508,9 +1290,6 @@
                     slideInfoFileOffset = this->authConstSlideInfo->subCacheFileOffset;
                     slideInfoFileSize   = this->authConstSlideInfo->usedFileSize;
                 }
-                break;
-            case Region::Kind::readOnly:
-                flags    = DYLD_CACHE_READ_ONLY_DATA;
                 break;
             case Region::Kind::linkedit:
                 flags    = 0;
@@ -1549,16 +1328,15 @@
 }
 
 void SubCache::writeCacheHeader(const BuilderOptions& options, const BuilderConfig& config,
-                                const std::span<CacheDylib> cacheDylibs,
-                                uint32_t osVersion, uint32_t altPlatform, uint32_t altOsVersion)
+                                const std::span<CacheDylib> cacheDylibs)
 {
     Chunk& cacheHeaderChunk = *this->cacheHeader.get();
     dyld_cache_header* dyldCacheHeader = (dyld_cache_header*)cacheHeaderChunk.subCacheBuffer;
 
     // "dyld_v1" + spaces + archName(), with enough spaces to pad to 15 bytes
     std::string magic = "dyld_v1";
-    magic.append(15 - magic.length() - strlen(options.arch.name()), ' ');
-    magic.append(options.arch.name());
+    magic.append(15 - magic.length() - strlen(options.archs.name()), ' ');
+    magic.append(options.archs.name());
     assert(magic.length() == 15);
 
     // Num of mappings depends on cache layout.
@@ -1604,14 +1382,12 @@
     dyldCacheHeader->progClosuresSize              = 0; // no longer used
     dyldCacheHeader->progClosuresTrieAddr          = 0; // no longer used
     dyldCacheHeader->progClosuresTrieSize          = 0; // no longer used
-    dyldCacheHeader->platform                      = options.platform.value();
+    dyldCacheHeader->platform                      = (uint8_t)options.platform;
     dyldCacheHeader->formatVersion                 = 0; //dyld3::closure::kFormatVersion;
     dyldCacheHeader->dylibsExpectedOnDisk          = !options.dylibsRemovedFromDisk;
     dyldCacheHeader->simulator                     = options.isSimulator();
     dyldCacheHeader->locallyBuiltCache             = options.isLocallyBuiltCache;
     dyldCacheHeader->builtFromChainedFixups        = false; // no longer used
-    dyldCacheHeader->newFormatTLVs                 = true;
-    dyldCacheHeader->padding                       = 0;
     dyldCacheHeader->sharedRegionStart             = this->subCacheVMAddress.rawValue();
     dyldCacheHeader->sharedRegionSize              = 0;
     dyldCacheHeader->maxSlide                      = 0; // overwritten later in build if the cache supports ASLR
@@ -1629,9 +1405,9 @@
     dyldCacheHeader->programsPBLSetPoolSize        = 0; // set later only on the main cache file
     dyldCacheHeader->programTrieAddr               = 0; // set later only on the main cache file
     dyldCacheHeader->programTrieSize               = 0; // set later only on the main cache file
-    dyldCacheHeader->osVersion                     = osVersion;
-    dyldCacheHeader->altPlatform                   = altPlatform;
-    dyldCacheHeader->altOsVersion                  = altOsVersion;
+    dyldCacheHeader->osVersion                     = 0; // set later only on the main cache file
+    dyldCacheHeader->altPlatform                   = 0; // set later only on the main cache file
+    dyldCacheHeader->altOsVersion                  = 0; // set later only on the main cache file
     dyldCacheHeader->swiftOptsOffset               = 0; // set later only on the main cache file
     dyldCacheHeader->swiftOptsSize                 = 0; // set later only on the main cache file
     dyldCacheHeader->subCacheArrayOffset           = 0;
@@ -1648,30 +1424,25 @@
     dyldCacheHeader->cacheAtlasSize                = 0; // set later only on the main cache file
     dyldCacheHeader->dynamicDataOffset             = 0; // set later only on the main cache file
     dyldCacheHeader->dynamicDataMaxSize            = 0; // set later only on the main cache file
-    dyldCacheHeader->tproMappingsOffset            = 0; // set later only on the main cache file
-    dyldCacheHeader->tproMappingsCount             = 0; // set later only on the main cache file
-    dyldCacheHeader->prewarmingDataOffset          = 0; // set later only on the main cache file
-    dyldCacheHeader->prewarmingDataSize            = 0; // set later only on the main cache file
 
     // Fill in old mappings
     // And new mappings which also have slide info
     this->writeCacheHeaderMappings();
 
-    this->addCacheHeaderImageInfo(options, config, cacheDylibs);
+    this->addCacheHeaderImageInfo(options, cacheDylibs);
 }
 
 void SubCache::addMainCacheHeaderInfo(const BuilderOptions& options, const BuilderConfig& config,
                                       const std::span<CacheDylib> cacheDylibs,
                                       CacheVMSize totalVMSize, uint64_t maxSlide,
+                                      uint32_t osVersion, uint32_t altPlatform, uint32_t altOsVersion,
                                       CacheVMAddress dyldInCacheUnslidAddr,
                                       CacheVMAddress dyldInCacheEntryUnslidAddr,
                                       const DylibTrieOptimizer& dylibTrieOptimizer,
                                       const ObjCOptimizer& objcOptimizer,
-                                      const SwiftOptimizer& swiftOpt,
+                                      const SwiftProtocolConformanceOptimizer& swiftProtocolConformanceOpt,
                                       const PatchTableOptimizer& patchTableOptimizer,
-                                      const FunctionVariantsOptimizer& functionVariantOptimizer,
-                                      const PrebuiltLoaderBuilder& prebuiltLoaderBuilder,
-                                      const PrewarmingOptimizer& prewarmingOptimizer)
+                                      const PrebuiltLoaderBuilder& prebuiltLoaderBuilder)
 {
     const CacheVMAddress cacheBaseAddress = config.layout.cacheBaseAddress;
 
@@ -1684,15 +1455,15 @@
     dyldCacheHeader->dylibsTrieAddr = dylibTrieOptimizer.dylibsTrieChunk->cacheVMAddress.rawValue();
     dyldCacheHeader->dylibsTrieSize = dylibTrieOptimizer.dylibsTrieChunk->subCacheFileSize.rawValue();
 
-    // Disable objc optimizations from EK shared cache
-    bool emitObjcOpts = !options.platform.isExclaveKit();
-    if ( !objcOptimizer.objcDylibs.empty() && emitObjcOpts ) {
+    if ( !objcOptimizer.objcDylibs.empty() ) {
         dyldCacheHeader->objcOptsOffset = (objcOptimizer.optsHeaderChunk->cacheVMAddress - cacheBaseAddress).rawValue();
         dyldCacheHeader->objcOptsSize   = objcOptimizer.optsHeaderChunk->subCacheFileSize.rawValue();
-
-        const auto& opt = swiftOpt;
+    }
+
+    if ( !objcOptimizer.objcDylibs.empty() ) {
+        const auto& opt = swiftProtocolConformanceOpt;
         dyldCacheHeader->swiftOptsOffset = (opt.optsHeaderChunk->cacheVMAddress - cacheBaseAddress).rawValue();
-        dyldCacheHeader->swiftOptsSize   = opt.optsHeaderChunk->subCacheFileSize.rawValue();
+        dyldCacheHeader->objcOptsSize    = opt.optsHeaderChunk->subCacheFileSize.rawValue();
     }
 
     dyldCacheHeader->patchInfoAddr = patchTableOptimizer.patchTableChunk->cacheVMAddress.rawValue();
@@ -1707,15 +1478,16 @@
     dyldCacheHeader->dyldInCacheMH      = dyldInCacheUnslidAddr.rawValue();
     dyldCacheHeader->dyldInCacheEntry   = dyldInCacheEntryUnslidAddr.rawValue();
 
+    dyldCacheHeader->osVersion      = osVersion;
+    dyldCacheHeader->altPlatform    = altPlatform;
+    dyldCacheHeader->altOsVersion   = altOsVersion;
+
     // record max slide now that final size is established
     dyldCacheHeader->maxSlide           = maxSlide;
 
     // TODO: Build the atlas
     dyldCacheHeader->cacheAtlasOffset              = 0; // set later only on the main cache file
     dyldCacheHeader->cacheAtlasSize                = 0; // set later only on the main cache file
-
-    dyldCacheHeader->functionVariantInfoAddr = functionVariantOptimizer.chunk->cacheVMAddress.rawValue();
-    dyldCacheHeader->functionVariantInfoSize = functionVariantOptimizer.fvInfoTotalByteSize;
 
     // The main cache has offsets to all the caches
     if ( !this->subCaches.empty() ) {
@@ -1736,30 +1508,6 @@
         dyldCacheHeader->dynamicDataOffset  = (dynamicConfig->cacheVMAddress - cacheBaseAddress).rawValue();
         dyldCacheHeader->dynamicDataMaxSize = dynamicConfig->cacheVMSize.rawValue();
     }
-
-    // Find all the TPRO regions
-    if ( dyldCacheHeader->tproMappingsCount != 0 ) {
-        assert(cacheHeaderChunk.subCacheFileOffset.rawValue() == 0);
-        auto* mappings = (dyld_cache_tpro_mapping_info*)((uint8_t*)dyldCacheHeader + dyldCacheHeader->tproMappingsOffset);
-        __block uint32_t index = 0;
-        forEachTPRORegion(config, this, this->subCaches, ^(CacheVMAddress firstChunkAddress, CacheVMAddress lastChunkAddress, CacheVMSize lastChunkSize) {
-            assert(index < dyldCacheHeader->tproMappingsCount);
-
-            CacheVMAddress vmAddr = firstChunkAddress;
-            CacheVMSize vmSize = (lastChunkAddress - firstChunkAddress) + lastChunkSize;
-
-            dyld_cache_tpro_mapping_info* mapping = & mappings[index];
-            mapping->unslidAddress = vmAddr.rawValue();
-            mapping->size = vmSize.rawValue();
-
-            ++index;
-        });
-    }
-
-    if ( prewarmingOptimizer.prewarmingChunk != nullptr ) {
-        dyldCacheHeader->prewarmingDataOffset = (prewarmingOptimizer.prewarmingChunk->cacheVMAddress - cacheBaseAddress).rawValue();
-        dyldCacheHeader->prewarmingDataSize   = prewarmingOptimizer.prewarmingChunk->subCacheFileSize.rawValue();
-    }
 }
 
 void SubCache::addSymbolsCacheHeaderInfo(const UnmappedSymbolsOptimizer& optimizer)
@@ -1783,10 +1531,9 @@
 }
 
 void SubCache::addCacheHeaderImageInfo(const BuilderOptions& options,
-                                       const BuilderConfig& config,
                                        const std::span<CacheDylib> cacheDylibs)
 {
-    if ( !this->needsCacheHeaderImageList(options) )
+    if ( !this->needsCacheHeaderImageList() )
         return;
 
     Chunk&             cacheHeaderChunk   = *this->cacheHeader.get();
@@ -1801,18 +1548,13 @@
     dyldCacheHeader->subCacheArrayOffset = (uint32_t)(dyldCacheHeader->imagesTextOffset + sizeof(dyld_cache_image_text_info) * cacheDylibs.size());
     dyldCacheHeader->subCacheArrayCount  = (uint32_t)this->subCaches.size();
 
-    // Always set the TPRO offset, but the count will only be set in the main cache
-    dyldCacheHeader->tproMappingsOffset = (uint32_t)(dyldCacheHeader->subCacheArrayOffset + sizeof(dyld_subcache_entry) * dyldCacheHeader->subCacheArrayCount);
-    if ( this->isMainCache() )
-        dyldCacheHeader->tproMappingsCount = numTPRORegions(config, this, this->subCaches);
-
     // calculate start of text image array and trailing string pool
     auto*    textImages   = (dyld_cache_image_text_info*)((uint8_t*)dyldCacheHeader + dyldCacheHeader->imagesTextOffset);
-    uint32_t stringOffset = (uint32_t)(dyldCacheHeader->tproMappingsOffset + sizeof(dyld_cache_tpro_mapping_info) * dyldCacheHeader->tproMappingsCount);
+    uint32_t stringOffset = (uint32_t)(dyldCacheHeader->subCacheArrayOffset + sizeof(dyld_subcache_entry) * dyldCacheHeader->subCacheArrayCount);
 
     // write text image array and image names pool at same time
     for ( const CacheDylib& cacheDylib : cacheDylibs ) {
-        cacheDylib.inputHdr->getUuid(textImages->uuid);
+        cacheDylib.inputMF->getUuid(textImages->uuid);
         textImages->loadAddress     = cacheDylib.cacheLoadAddress.rawValue();
         textImages->textSegmentSize = (uint32_t)cacheDylib.segments.front().cacheVMSize.rawValue();
         textImages->pathOffset      = stringOffset;
@@ -1920,17 +1662,16 @@
     return this->kind == Kind::stubsCustomer;
 }
 
-bool SubCache::needsCacheHeaderImageList(const BuilderOptions& options) const
+bool SubCache::needsCacheHeaderImageList() const
 {
     // Symbols and stubs files don't need an image list
+    // We'd like to not add the image list to subcaches, only the main cache, but Rosetta needs
+    // the image list on subCaches.
     switch ( this->kind ) {
         case Kind::mainDevelopment:
         case Kind::mainCustomer:
+        case Kind::subUniversal:
             return true;
-        case Kind::subUniversal:
-            // We'd like to not add the image list to subcaches, only the main cache, but Rosetta needs
-            // the image list on subCaches.
-            return options.arch.sameCpu(mach_o::Architecture::x86_64);
         case Kind::stubsDevelopment:
         case Kind::stubsCustomer:
         case Kind::symbols:
@@ -2053,9 +1794,6 @@
     __block MachOFile::ChainedFixupPointerOnDisk* lastFixup = nullptr;
     __block uint64_t lastPageIndex = ~0ULL;
     for ( Chunk* chunk : region.chunks ) {
-        if ( chunk->isAlignChunk() )
-            continue;
-
         SlidChunk* slidChunk = chunk->isSlidChunk();
 
         slidChunk->tracker.forEachFixup(^(void *loc, bool& stop) {
@@ -2232,110 +1970,6 @@
     return Error();
 }
 
-Error SubCache::computeSlideInfoV5(const BuilderConfig&           config,
-                                   cache_builder::SlideInfoChunk* slideChunk,
-                                   Region&                        region)
-{
-    __block Diagnostics diag;
-
-    bool canContainAuthPointers = region.canContainAuthPointers();
-
-    assert((region.subCacheVMSize.rawValue() % config.slideInfo.slideInfoPageSize) == 0);
-    dyld_cache_slide_info5* info    = (dyld_cache_slide_info5*)slideChunk->subCacheBuffer;
-    info->version                   = 5;
-    info->page_size                 = config.slideInfo.slideInfoPageSize;
-    info->page_starts_count         = (uint32_t)region.subCacheVMSize.rawValue() / config.slideInfo.slideInfoPageSize;
-    info->value_add                 = config.layout.cacheBaseAddress.rawValue();
-
-    assert((sizeof(dyld_cache_slide_info5) + (info->page_starts_count * sizeof(uint16_t))) <= slideChunk->cacheVMSize.rawValue());
-
-    std::fill(&info->page_starts[0], &info->page_starts[info->page_starts_count], DYLD_CACHE_SLIDE_V5_PAGE_ATTR_NO_REBASE);
-
-    // Walk each fixup in each segment.  Every time we cross a page, add a page start
-    __block dyld_cache_slide_pointer5* lastFixup     = nullptr;
-    __block uint64_t                   lastPageIndex = ~0ULL;
-    for ( Chunk* chunk : region.chunks ) {
-        SlidChunk* slidChunk = chunk->isSlidChunk();
-
-        slidChunk->tracker.forEachFixup(^(void *loc, bool& stop) {
-            // V5 fixups must be 8-byte aligned
-            assert(((uint64_t)loc % 8) == 0);
-
-            VMOffset       vmOffsetInSegment((uint64_t)loc - (uint64_t)slidChunk->subCacheBuffer);
-            CacheVMAddress fixupVMAddr = slidChunk->cacheVMAddress + vmOffsetInSegment;
-            uint64_t       pageIndex   = (fixupVMAddr - region.subCacheVMAddress).rawValue() / info->page_size;
-
-            // If we are on a new page, then start a new chain
-            if ( pageIndex != lastPageIndex ) {
-                uint64_t vmOffsetInPage      = fixupVMAddr.rawValue() % info->page_size;
-                info->page_starts[pageIndex] = vmOffsetInPage;
-            }
-            else {
-                // Patch the previous fixup on this page to point to this one
-                lastFixup->auth.next = ((uint64_t)loc - (uint64_t)lastFixup) / 8;
-            }
-
-            dyld_cache_slide_pointer5* fixupLocation = (dyld_cache_slide_pointer5*)loc;
-
-            // Convert this fixup from the chained format in the cache builder, to the version we want in the cache file
-            CacheVMAddress vmAddr = Fixup::Cache64::getCacheVMAddressFromLocation(config.layout.cacheBaseAddress,
-                                                                                  loc);
-
-            uint8_t high8 = Fixup::Cache64::getHigh8(loc);
-            VMOffset cacheVMOffset = vmAddr - config.layout.cacheBaseAddress;
-
-            uint16_t    authDiversity  = 0;
-            bool        authIsAddr     = false;
-            uint8_t     authKey        = 0;
-            if ( Fixup::Cache64::hasAuthData(loc, authDiversity, authIsAddr, authKey) ) {
-                // Authenticated value
-                assert(high8 == 0);
-                assert(canContainAuthPointers);
-                assert((authKey == 0) || (authKey == 2)); // IA (0) or DA (2)
-
-                fixupLocation->auth.runtimeOffset       = cacheVMOffset.rawValue();
-                fixupLocation->auth.diversity           = authDiversity;
-                fixupLocation->auth.addrDiv             = authIsAddr ? 1 : 0;
-                fixupLocation->auth.keyIsData           = (authKey == 2) ? 1 : 0;
-                fixupLocation->auth.next                = 0;
-                fixupLocation->auth.auth                = 1;
-
-                if ( fixupLocation->auth.runtimeOffset != cacheVMOffset.rawValue() ) {
-                    diag.error("cache offset 0x%llx exceeds v5 format", cacheVMOffset.rawValue());
-                    stop = true;
-                    return;
-                }
-            } else {
-                // Unauthenticated value
-                fixupLocation->regular.runtimeOffset    = cacheVMOffset.rawValue();
-                fixupLocation->regular.high8            = high8;
-                fixupLocation->regular.next             = 0;
-                fixupLocation->regular.unused           = 0;
-                fixupLocation->regular.auth             = 0;
-
-                if ( fixupLocation->regular.runtimeOffset != cacheVMOffset.rawValue() ) {
-                    diag.error("cache offset 0x%llx exceeds v5 format", cacheVMOffset.rawValue());
-                    stop = true;
-                    return;
-                }
-            }
-
-            lastFixup     = fixupLocation;
-            lastPageIndex = pageIndex;
-        });
-        if ( diag.hasError() )
-            break;
-    }
-
-    if ( diag.hasError() )
-        return Error("could not build slide info because: %s", diag.errorMessageCStr());
-
-    // V5 doesn't deduplicate content like V1, so the used size is the original size too
-    slideChunk->usedFileSize = slideChunk->subCacheFileSize;
-
-    return Error();
-}
-
 Error SubCache::computeSlideInfoForRegion(const BuilderConfig&           config,
                                           cache_builder::SlideInfoChunk* slideChunk,
                                           Region&                        region)
@@ -2347,8 +1981,6 @@
             return computeSlideInfoV2(config, slideChunk, region);
         case cache_builder::SlideInfo::SlideInfoFormat::v3:
             return computeSlideInfoV3(config, slideChunk, region);
-        case cache_builder::SlideInfo::SlideInfoFormat::v5:
-            return computeSlideInfoV5(config, slideChunk, region);
     }
 
     return Error();
@@ -2359,10 +1991,8 @@
     Diagnostics diag;
 
     for ( Chunk* chunk : region.chunks ) {
-        if ( chunk->isAlignChunk() )
-            continue;
-
         SlidChunk* slidChunk = chunk->isSlidChunk();
+
         slidChunk->tracker.forEachFixup(^(void *loc, bool& stop) {
             if ( config.layout.is64 ) {
                 CacheVMAddress vmAddr = Fixup::Cache64::getCacheVMAddressFromLocation(config.layout.cacheBaseAddress, loc);
@@ -2389,12 +2019,6 @@
             case Region::Kind::text:
                 // No slide info for text
                 break;
-            case Region::Kind::tproConst:
-                if ( config.slideInfo.slideInfoFormat.has_value() )
-                    err = computeSlideInfoForRegion(config, this->tproConstSlideInfo.get(), region);
-                else
-                    err = convertChainsToVMAddresses(config, region);
-                break;
             case Region::Kind::data:
                 if ( config.slideInfo.slideInfoFormat.has_value() )
                     err = computeSlideInfoForRegion(config, this->dataSlideInfo.get(), region);
@@ -2407,12 +2031,6 @@
                 else
                     err = convertChainsToVMAddresses(config, region);
                 break;
-            case Region::Kind::tproAuthConst:
-                if ( config.slideInfo.slideInfoFormat.has_value() )
-                    err = computeSlideInfoForRegion(config, this->tproAuthConstSlideInfo.get(), region);
-                else
-                    err = convertChainsToVMAddresses(config, region);
-                break;
             case Region::Kind::auth:
                 if ( config.slideInfo.slideInfoFormat.has_value() )
                     err = computeSlideInfoForRegion(config, this->authSlideInfo.get(), region);
@@ -2425,9 +2043,8 @@
                 else
                     err = convertChainsToVMAddresses(config, region);
                 break;
-            case Region::Kind::readOnly:
             case Region::Kind::linkedit:
-                // No slide info for linkedit
+                // No slide info for text
                 break;
             case Region::Kind::unmapped:
             case Region::Kind::dynamicConfig: