Loading...
common/DyldSharedCache.cpp dyld-1122.1 dyld-960
--- dyld/dyld-1122.1/common/DyldSharedCache.cpp
+++ dyld/dyld-960/common/DyldSharedCache.cpp
@@ -22,9 +22,6 @@
  * @APPLE_LICENSE_HEADER_END@
  */
 
-#include <TargetConditionals.h>
-
-#if !TARGET_OS_EXCLAVEKIT
 
 #include <dirent.h>
 #include <sys/errno.h>
@@ -45,7 +42,7 @@
 #include <vector>
 #include <unordered_map>
 #include <unordered_set>
-// #include "SharedCacheBuilder.h"
+#include "SharedCacheBuilder.h"
 #include "FileUtils.h"
 #endif
 
@@ -64,18 +61,14 @@
 #include <sstream>
 #endif
 
-using dyld3::MachOFile;
-using dyld3::MachOLoaded;
-using dyld3::MachOAnalyzer;
 using dyld4::PrebuiltLoader;
 using dyld4::PrebuiltLoaderSet;
-
 #if (BUILDING_LIBDYLD || BUILDING_DYLD)
 VIS_HIDDEN bool gEnableSharedCacheDataConst = false;
 #endif
 
 
-#if 0 // BUILDING_CACHE_BUILDER
+#if BUILDING_CACHE_BUILDER
 DyldSharedCache::CreateResults DyldSharedCache::create(const CreateOptions&               options,
                                                        const dyld3::closure::FileSystem&  fileSystem,
                                                        const std::vector<MappedMachO>&    dylibsToCache,
@@ -237,17 +230,10 @@
     return (const T)(addr + slide);
 }
 
-const char* DyldSharedCache::getCacheTypeName(uint64_t cacheType) {
-    switch ( cacheType ) {
-        case kDyldSharedCacheTypeDevelopment:
-            return "development";
-        case kDyldSharedCacheTypeProduction:
-            return "production";
-        case kDyldSharedCacheTypeUniversal:
-            return "universal";
-        default:
-            return "unknown";
-    }
+uint64_t DyldSharedCache::getCodeSignAddress() const
+{
+    auto mappings = (const dyld_cache_mapping_info*)((uint8_t*)this + header.mappingOffset);
+    return mappings[header.mappingCount-1].address + mappings[header.mappingCount-1].size;
 }
 
 void DyldSharedCache::forEachRegion(void (^handler)(const void* content, uint64_t vmAddr, uint64_t size,
@@ -295,11 +281,7 @@
                         uint32_t initProt, uint32_t maxProt, uint64_t flags, bool& stopRegion) {
             const char* mappingName = "";
             if ( maxProt & VM_PROT_EXECUTE ) {
-                if ( flags & DYLD_CACHE_MAPPING_TEXT_STUBS ) {
-                    mappingName = "__TEXT_STUBS";
-                } else {
-                    mappingName = "__TEXT";
-                }
+                mappingName = "__TEXT";
             } else if ( maxProt & VM_PROT_WRITE ) {
                 if ( flags & DYLD_CACHE_MAPPING_AUTH_DATA ) {
                     if ( flags & DYLD_CACHE_MAPPING_DIRTY_DATA )
@@ -354,8 +336,9 @@
     if ( header.mappingOffset <= __offsetof(dyld_cache_header, subCacheArrayCount) )
         return;
 
+    const dyld_subcache_entry* subCaches = (const dyld_subcache_entry*)((uint8_t*)this + header.subCacheArrayOffset);
     for (uint32_t i = 0; i != header.subCacheArrayCount; ++i) {
-        const DyldSharedCache* cache = (const DyldSharedCache*)((uintptr_t)this + this->getSubCacheVmOffset(i));
+        const DyldSharedCache* cache = (const DyldSharedCache*)((uint8_t*)this + subCaches[i].cacheVMOffset);
         handler(cache, stop);
         if ( stop )
             return;
@@ -368,43 +351,6 @@
         return 0;
 
     return header.subCacheArrayCount;
-}
-
-int32_t DyldSharedCache::getSubCacheIndex(const void* addr) const
-{
-    __block int32_t index = 0;
-    __block bool found = false;
-    this->forEachCache(^(const DyldSharedCache *cache, bool &stopCache) {
-        bool readOnly = false;
-        if ( cache->inCache(addr, sizeof(uint64_t), readOnly) ) {
-            stopCache = true;
-            found = true;
-            return;
-        }
-        index++;
-    });
-    int32_t result = found ? index : -1;
-    return result;
-}
-
-void DyldSharedCache::getSubCacheUuid(uint8_t index, uint8_t uuid[]) const {
-    if (header.mappingOffset <= __offsetof(dyld_cache_header, cacheSubType) ) {
-        const dyld_subcache_entry_v1* subCacheEntries = (dyld_subcache_entry_v1*)((uintptr_t)this + header.subCacheArrayOffset);
-        memcpy(uuid, subCacheEntries[index].uuid, 16);
-    } else {
-        const dyld_subcache_entry* subCacheEntries = (dyld_subcache_entry*)((uintptr_t)this + header.subCacheArrayOffset);
-        memcpy(uuid, subCacheEntries[index].uuid, 16);
-    }
-}
-
-uint64_t DyldSharedCache::getSubCacheVmOffset(uint8_t index) const {
-    if (header.mappingOffset <= __offsetof(dyld_cache_header, cacheSubType) ) {
-        const dyld_subcache_entry_v1* subCacheEntries = (dyld_subcache_entry_v1*)((uintptr_t)this + header.subCacheArrayOffset);
-        return subCacheEntries[index].cacheVMOffset;
-    } else {
-        const dyld_subcache_entry* subCacheEntries = (dyld_subcache_entry*)((uintptr_t)this + header.subCacheArrayOffset);
-        return subCacheEntries[index].cacheVMOffset;
-    }
 }
 
 bool DyldSharedCache::inCache(const void* addr, size_t length, bool& readOnly) const
@@ -545,7 +491,7 @@
     // check for cache without local symbols info
     if (!this->hasLocalSymbolsInfo())
         return nullptr;
-    const auto localInfo = (dyld_cache_local_symbols_info*)((uintptr_t)this + header.localSymbolsOffset);
+    const auto localInfo = (dyld_cache_local_symbols_info*)((uint8_t*)this + header.localSymbolsOffset);
     return getLocalNlistEntries(localInfo);
 }
 
@@ -554,7 +500,7 @@
     // check for cache without local symbols info
      if (!this->hasLocalSymbolsInfo())
         return 0;
-    const auto localInfo = (dyld_cache_local_symbols_info*)((uintptr_t)this + header.localSymbolsOffset);
+    const auto localInfo = (dyld_cache_local_symbols_info*)((uint8_t*)this + header.localSymbolsOffset);
     return localInfo->nlistCount;
 }
 
@@ -568,7 +514,7 @@
     // check for cache without local symbols info
      if (!this->hasLocalSymbolsInfo())
         return nullptr;
-    const auto localInfo = (dyld_cache_local_symbols_info*)((uintptr_t)this + header.localSymbolsOffset);
+    const auto localInfo = (dyld_cache_local_symbols_info*)((uint8_t*)this + header.localSymbolsOffset);
     return getLocalStrings(localInfo);
 }
 
@@ -577,7 +523,7 @@
     // check for cache without local symbols info
      if (!this->hasLocalSymbolsInfo())
         return 0;
-    const auto localInfo = (dyld_cache_local_symbols_info*)((uintptr_t)this + header.localSymbolsOffset);
+    const auto localInfo = (dyld_cache_local_symbols_info*)((uint8_t*)this + header.localSymbolsOffset);
     return localInfo->stringsSize;
 }
 
@@ -587,7 +533,7 @@
     const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
     uint32_t dyldCacheImageIndex;
     if ( hasImagePath(dylibPath, dyldCacheImageIndex) )
-        return (dyld3::MachOFile*)((uintptr_t)this + dylibs[dyldCacheImageIndex].address - mappings[0].address);
+        return (dyld3::MachOFile*)((uint8_t*)this + dylibs[dyldCacheImageIndex].address - mappings[0].address);
     return nullptr;
 }
 
@@ -596,7 +542,7 @@
     // check for cache without local symbols info
     if (!this->hasLocalSymbolsInfo())
         return;
-    const auto localInfo = (dyld_cache_local_symbols_info*)((uintptr_t)this + header.localSymbolsOffset);
+    const auto localInfo = (dyld_cache_local_symbols_info*)((uint8_t*)this + header.localSymbolsOffset);
 
     if ( header.mappingOffset >= __offsetof(dyld_cache_header, symbolFileUUID) ) {
         // On new caches, the dylibOffset is 64-bits, and is a VM offset
@@ -621,17 +567,10 @@
 const mach_header* DyldSharedCache::getIndexedImageEntry(uint32_t index, uint64_t& mTime, uint64_t& inode) const
 {
     const dyld_cache_image_info*   dylibs   = images();
-    const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((uintptr_t)this + header.mappingOffset);
+    const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
     mTime = dylibs[index].modTime;
     inode = dylibs[index].inode;
-    return (mach_header*)((uintptr_t)this + dylibs[index].address - mappings[0].address);
-}
-
-const mach_header* DyldSharedCache::getIndexedImageEntry(uint32_t index) const
-{
-    uint64_t mTime = 0;
-    uint64_t inode = 0;
-    return this->getIndexedImageEntry(index, mTime, inode);
+    return (mach_header*)((uint8_t*)this + dylibs[index].address - mappings[0].address);
 }
 
 
@@ -707,9 +646,9 @@
         else if ( maxProt == VM_PROT_READ )
             prot = "RO";
         if ( size > 1024*1024 )
-            snprintf(lineBuffer, sizeof(lineBuffer), "mapping  %s %4lluMB 0x%0llX -> 0x%0llX\n", prot, size/(1024*1024), vmAddr, vmAddr+size);
+            sprintf(lineBuffer, "mapping  %s %4lluMB 0x%0llX -> 0x%0llX\n", prot, size/(1024*1024), vmAddr, vmAddr+size);
         else
-            snprintf(lineBuffer, sizeof(lineBuffer), "mapping  %s %4lluKB 0x%0llX -> 0x%0llX\n", prot, size/1024,        vmAddr, vmAddr+size);
+            sprintf(lineBuffer, "mapping  %s %4lluKB 0x%0llX -> 0x%0llX\n", prot, size/1024,        vmAddr, vmAddr+size);
         result += lineBuffer;
     });
 
@@ -721,7 +660,7 @@
         const dyld3::MachOFile* mf = (dyld3::MachOFile*)mh;
         mf->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& info, bool& stop) {
             char lineBuffer[256];
-            snprintf(lineBuffer, sizeof(lineBuffer), "\t%16s 0x%08llX -> 0x%08llX\n", info.segName, info.vmAddr, info.vmAddr+info.vmSize);
+            sprintf(lineBuffer, "\t%16s 0x%08llX -> 0x%08llX\n", info.segName, info.vmAddr, info.vmAddr+info.vmSize);
             result += lineBuffer;
         });
         result += "\n";
@@ -819,15 +758,41 @@
     return false;
 }
 
+bool DyldSharedCache::isOverridablePath(const char* dylibPath) const
+{
+    // all dylibs in customer dyld cache cannot be overridden except libdispatch.dylib
+    if ( header.cacheType == kDyldSharedCacheTypeProduction ) {
+        return (strcmp(dylibPath, "/usr/lib/system/libdispatch.dylib") == 0);
+    }
+    // in dev caches we can override all paths
+    return true;
+}
+
+bool DyldSharedCache::hasNonOverridablePath(const char* dylibPath) const
+{
+    // all dylibs in customer dyld cache cannot be overridden except libdispatch.dylib
+    bool pathIsInDyldCacheWhichCannotBeOverridden = false;
+    if ( header.cacheType == kDyldSharedCacheTypeProduction ) {
+        uint32_t imageIndex;
+        pathIsInDyldCacheWhichCannotBeOverridden = this->hasImagePath(dylibPath, imageIndex);
+        if ( pathIsInDyldCacheWhichCannotBeOverridden && isOverridablePath(dylibPath) )
+            pathIsInDyldCacheWhichCannotBeOverridden = false;
+    }
+    return pathIsInDyldCacheWhichCannotBeOverridden;
+}
+
 intptr_t DyldSharedCache::slide() const
 {
     const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
     return (intptr_t)this - (intptr_t)(mappings[0].address);
 }
 
-const PrebuiltLoaderSet* DyldSharedCache::dylibsLoaderSet() const
+const PrebuiltLoader* DyldSharedCache::findPrebuiltLoader(const char* path) const
 {
     if ( header.mappingOffset < __offsetof(dyld_cache_header, programTrieSize) )
+        return nullptr;
+    uint32_t imageIndex;
+    if ( !this->hasImagePath(path, imageIndex) )
         return nullptr;
     const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
     if ( mappings[0].fileOffset != 0 )
@@ -838,20 +803,7 @@
         return nullptr;
     uintptr_t                slide        = (uintptr_t)this - (uintptr_t)(mappings[0].address);
     const PrebuiltLoaderSet* pbLoaderSet  = (PrebuiltLoaderSet*)(this->header.dylibsPBLSetAddr + slide);
-    return pbLoaderSet;
-}
-
-const PrebuiltLoader* DyldSharedCache::findPrebuiltLoader(const char* path) const
-{
-    if ( header.mappingOffset < __offsetof(dyld_cache_header, programTrieSize) )
-        return nullptr;
-    uint32_t imageIndex;
-    if ( !this->hasImagePath(path, imageIndex) )
-        return nullptr;
-    if ( const PrebuiltLoaderSet* pbLoaderSet = this->dylibsLoaderSet() )
-        return pbLoaderSet->atIndex(imageIndex);
-
-    return nullptr;
+    return pbLoaderSet->atIndex(imageIndex);
 }
 
 void DyldSharedCache::forEachLaunchLoaderSet(void (^handler)(const char* executableRuntimePath, const PrebuiltLoaderSet* pbls)) const
@@ -901,22 +853,17 @@
 
 bool DyldSharedCache::hasLaunchLoaderSetWithCDHash(const char* cdHashString) const
 {
-    return (findLaunchLoaderSetWithCDHash(cdHashString) != nullptr);
-}
-
-const dyld4::PrebuiltLoaderSet* DyldSharedCache::findLaunchLoaderSetWithCDHash(const char* cdHashString) const
-{
     if ( cdHashString == nullptr )
-        return nullptr;
+        return false;
 
     // Check source doesn't overflow buffer.  strncat unfortunately isn't available
     if ( strlen(cdHashString) >= 128 )
-        return nullptr;
-
-    char cdPath[140];
-    strlcpy(cdPath, "/cdhash/", sizeof(cdPath));
-    strlcat(cdPath, cdHashString, sizeof(cdPath));
-    return findLaunchLoaderSet(cdPath);
+        return false;
+
+    char buffer[128] = { '\0' };
+    strcat(buffer, "/cdhash/");
+    strlcat(buffer, cdHashString, sizeof(buffer));
+    return findLaunchLoaderSet(buffer) != nullptr;
 }
 
 
@@ -1054,11 +1001,6 @@
     }
 }
 
-const void* DyldSharedCache::patchTable() const
-{
-    return getAddrField<const void*>(header.patchInfoAddr);
-}
-
 uint32_t DyldSharedCache::patchInfoVersion() const {
     if ( header.mappingOffset <= __offsetof(dyld_cache_header, swiftOptsSize) ) {
         return 1;
@@ -1084,13 +1026,25 @@
         return patchArray[imageIndex].patchExportsCount;
     }
 
-    // V2/V3 and newer structs
-    PatchTable patchTable(this->patchTable(), header.patchInfoAddr);
-    return patchTable.patchableExportCount(imageIndex);
-}
-
-void DyldSharedCache::forEachPatchableExport(uint32_t imageIndex, void (^handler)(uint32_t dylibVMOffsetOfImpl, const char* exportName,
-                                                                                  PatchKind patchKind)) const {
+    // V2 and newer structs
+    if ( patchVersion == 2 ) {
+        const dyld_cache_patch_info_v2* patchInfo = getAddrField<dyld_cache_patch_info_v2*>(header.patchInfoAddr);
+        const dyld_cache_image_patches_v2* patchArray = getAddrField<dyld_cache_image_patches_v2*>(patchInfo->patchTableArrayAddr);
+
+        // FIXME: This shouldn't be possible.  The cache builder always adds 1 dyld_cache_image_patches_v2 per-image.  But for now
+        // be conservative to match V1.
+        if (imageIndex > patchInfo->patchTableArrayCount)
+            return 0;
+
+        const dyld_cache_image_patches_v2& imagePatches = patchArray[imageIndex];
+        return imagePatches.patchExportsCount;
+    }
+
+    // Unknown version
+    assert(false);
+}
+
+void DyldSharedCache::forEachPatchableExport(uint32_t imageIndex, void (^handler)(uint32_t dylibVMOffsetOfImpl, const char* exportName)) const {
     if ( header.patchInfoAddr == 0 )
         return;
 
@@ -1124,22 +1078,43 @@
 
             // Convert from a cache offset to an offset from the input image
             uint32_t imageOffset = (uint32_t)((cacheUnslidAddress + patchExport.cacheOffsetOfImpl) - imageLoadAddress);
-            handler(imageOffset, exportName, PatchKind::regular);
-        }
-
-        return;
-    }
-
-    // V2 newer structs
-    PatchTable patchTable(this->patchTable(), header.patchInfoAddr);
-    patchTable.forEachPatchableExport(imageIndex, handler);
-}
-
-#if BUILDING_SHARED_CACHE_UTIL
+            handler(imageOffset, exportName);
+        }
+
+        return;
+    }
+
+    // V2 and newer structs
+    if ( patchVersion == 2 ) {
+        const auto* patchInfo = getAddrField<dyld_cache_patch_info_v2*>(header.patchInfoAddr);
+        const auto* patchArray = getAddrField<dyld_cache_image_patches_v2*>(patchInfo->patchTableArrayAddr);
+        const auto* imageExports = getAddrField<dyld_cache_image_export_v2*>(patchInfo->patchImageExportsArrayAddr);
+        const char* exportNames = getAddrField<char*>(patchInfo->patchExportNamesAddr);
+
+        // FIXME: This shouldn't be possible.  The cache builder always adds 1 dyld_cache_image_patches_v2 per-image.  But for now
+        // be conservative to match V1.
+        if (imageIndex > patchInfo->patchTableArrayCount)
+            return;
+
+        const dyld_cache_image_patches_v2& imagePatches = patchArray[imageIndex];
+        // FIXME: This shouldn't be possible.  We should catch this in the builder instead
+        if ( (imagePatches.patchExportsStartIndex + imagePatches.patchExportsCount) > patchInfo->patchImageExportsArrayCount )
+            return;
+
+        for (uint64_t exportIndex = 0; exportIndex != imagePatches.patchExportsCount; ++exportIndex) {
+            const dyld_cache_image_export_v2& imageExport = imageExports[imagePatches.patchExportsStartIndex + exportIndex];
+            const char* exportName = ( imageExport.exportNameOffset < patchInfo->patchExportNamesSize ) ? &exportNames[imageExport.exportNameOffset] : "";
+            handler(imageExport.dylibOffsetOfImpl, exportName);
+        }
+        return;
+    }
+
+    assert(false);
+}
+
 void DyldSharedCache::forEachPatchableUseOfExport(uint32_t imageIndex, uint32_t dylibVMOffsetOfImpl,
                                                   void (^handler)(uint32_t userImageIndex, uint32_t userVMOffset,
-                                                                  MachOLoaded::PointerMetaData pmd, uint64_t addend,
-                                                                  bool isWeakImport)) const {
+                                                                  MachOLoaded::PointerMetaData pmd, uint64_t addend)) const {
     if ( header.patchInfoAddr == 0 )
         return;
 
@@ -1245,17 +1220,72 @@
                 pmd.key               = patchLocation.key;
                 pmd.usesAddrDiversity = patchLocation.usesAddressDiversity;
 
-                handler(userImageIndex, userVMOffset, pmd, patchLocation.getAddend(), false);
+                handler(userImageIndex, userVMOffset, pmd, patchLocation.getAddend());
             }
         }
         return;
     }
 
-    // V2/V3 and newer structs
-    PatchTable patchTable(this->patchTable(), header.patchInfoAddr);
-    patchTable.forEachPatchableUseOfExport(imageIndex, dylibVMOffsetOfImpl, handler);
-}
-#endif
+    // V2 and newer structs
+    if ( patchVersion == 2 ) {
+        const auto* patchInfo = getAddrField<dyld_cache_patch_info_v2*>(header.patchInfoAddr);
+        const auto* patchArray = getAddrField<dyld_cache_image_patches_v2*>(patchInfo->patchTableArrayAddr);
+        const auto* imageExports = getAddrField<dyld_cache_image_export_v2*>(patchInfo->patchImageExportsArrayAddr);
+        const auto* clientArray = getAddrField<dyld_cache_image_clients_v2*>(patchInfo->patchClientsArrayAddr);
+        const auto* clientExports = getAddrField<dyld_cache_patchable_export_v2*>(patchInfo->patchClientExportsArrayAddr);
+        const auto* patchLocations = getAddrField<dyld_cache_patchable_location_v2*>(patchInfo->patchLocationArrayAddr);
+
+        // FIXME: This shouldn't be possible.  The cache builder always adds 1 dyld_cache_image_patches_v2 per-image.  But for now
+        // be conservative to match V1.
+        if (imageIndex > patchInfo->patchTableArrayCount)
+            return;
+
+        const dyld_cache_image_patches_v2& imagePatches = patchArray[imageIndex];
+        // FIXME: This shouldn't be possible.  We should catch this in the builder instead
+        if ( (imagePatches.patchClientsStartIndex + imagePatches.patchClientsCount) > patchInfo->patchClientsArrayCount )
+            return;
+
+        for (uint64_t clientIndex = 0; clientIndex != imagePatches.patchClientsCount; ++clientIndex) {
+            const dyld_cache_image_clients_v2& client = clientArray[imagePatches.patchClientsStartIndex + clientIndex];
+
+            // FIXME: This shouldn't be possible.  We should catch this in the builder instead
+            if ( (client.patchExportsStartIndex + client.patchExportsCount) > patchInfo->patchClientExportsArrayCount )
+                return;
+
+            for (uint64_t exportIndex = 0; exportIndex != client.patchExportsCount; ++exportIndex) {
+                const dyld_cache_patchable_export_v2& clientExport = clientExports[client.patchExportsStartIndex + exportIndex];
+
+                // The client export points to an image export from the image we are rooting
+                if ( clientExport.imageExportIndex > patchInfo->patchImageExportsArrayCount )
+                    return;
+
+                const dyld_cache_image_export_v2& imageExport = imageExports[clientExport.imageExportIndex];
+
+                if ( imageExport.dylibOffsetOfImpl != dylibVMOffsetOfImpl )
+                    continue;
+
+                if ( (clientExport.patchLocationsStartIndex + clientExport.patchLocationsCount) > patchInfo->patchLocationArrayCount )
+                    return;
+
+                for (uint64_t locationIndex = 0; locationIndex != clientExport.patchLocationsCount; ++locationIndex) {
+                    const dyld_cache_patchable_location_v2& patchLocation = patchLocations[clientExport.patchLocationsStartIndex + locationIndex];
+
+                    dyld3::MachOLoaded::PointerMetaData pmd;
+                    pmd.diversity         = patchLocation.discriminator;
+                    pmd.high8             = patchLocation.high7 << 1;
+                    pmd.authenticated     = patchLocation.authenticated;
+                    pmd.key               = patchLocation.key;
+                    pmd.usesAddrDiversity = patchLocation.usesAddressDiversity;
+
+                    handler(client.clientDylibIndex, patchLocation.dylibOffsetOfUse, pmd, patchLocation.getAddend());
+                }
+            }
+        }
+        return;
+    }
+
+    assert(false);
+}
 
 bool DyldSharedCache::shouldPatchClientOfImage(uint32_t imageIndex, uint32_t userImageIndex) const {
     if ( header.patchInfoAddr == 0 )
@@ -1269,14 +1299,37 @@
         return false;
     }
 
-    // V2/V3 and newer structs
-    PatchTable patchTable(this->patchTable(), header.patchInfoAddr);
-    return patchTable.imageHasClient(imageIndex, userImageIndex);
+    // V2 and newer structs
+    if ( patchVersion == 2 ) {
+        const auto* patchInfo = getAddrField<dyld_cache_patch_info_v2*>(header.patchInfoAddr);
+        const auto* patchArray = getAddrField<dyld_cache_image_patches_v2*>(patchInfo->patchTableArrayAddr);
+        const auto* clientArray = getAddrField<dyld_cache_image_clients_v2*>(patchInfo->patchClientsArrayAddr);
+
+        // FIXME: This shouldn't be possible.  The cache builder always adds 1 dyld_cache_image_patches_v2 per-image.  But for now
+        // be conservative to match V1.
+        if (imageIndex > patchInfo->patchTableArrayCount)
+            return false;
+
+        const dyld_cache_image_patches_v2& imagePatches = patchArray[imageIndex];
+        // FIXME: This shouldn't be possible.  We should catch this in the builder instead
+        if ( (imagePatches.patchClientsStartIndex + imagePatches.patchClientsCount) > patchInfo->patchClientsArrayCount )
+            return false;
+
+        for (uint64_t clientIndex = 0; clientIndex != imagePatches.patchClientsCount; ++clientIndex) {
+            const dyld_cache_image_clients_v2& client = clientArray[imagePatches.patchClientsStartIndex + clientIndex];
+
+            // We only want fixups in a specific image.  Skip any others
+            if ( client.clientDylibIndex == userImageIndex )
+                return true;
+        }
+        return false;
+    }
+
+    assert(false);
 }
 
 void DyldSharedCache::forEachPatchableUseOfExportInImage(uint32_t imageIndex, uint32_t dylibVMOffsetOfImpl, uint32_t userImageIndex,
-                                                         void (^handler)(uint32_t userVMOffset, MachOLoaded::PointerMetaData pmd, uint64_t addend,
-                                                                         bool isWeakImport)) const {
+                                                         void (^handler)(uint32_t userVMOffset, MachOLoaded::PointerMetaData pmd, uint64_t addend)) const {
     if ( header.patchInfoAddr == 0 )
         return;
 
@@ -1384,30 +1437,88 @@
                     pmd.key               = patchLocation.key;
                     pmd.usesAddrDiversity = patchLocation.usesAddressDiversity;
 
-                    handler(userVMOffset, pmd, patchLocation.getAddend(), false);
+                    handler(userVMOffset, pmd, patchLocation.getAddend());
                 }
             }
         }
         return;
     }
 
-    // V2/V3 and newer structs
-    PatchTable patchTable(this->patchTable(), header.patchInfoAddr);
-    patchTable.forEachPatchableUseOfExportInImage(imageIndex, dylibVMOffsetOfImpl,
-                                                  userImageIndex, handler);
+    // V2 and newer structs
+    if ( patchVersion == 2 ) {
+        const auto* patchInfo = getAddrField<dyld_cache_patch_info_v2*>(header.patchInfoAddr);
+        const auto* patchArray = getAddrField<dyld_cache_image_patches_v2*>(patchInfo->patchTableArrayAddr);
+        const auto* imageExports = getAddrField<dyld_cache_image_export_v2*>(patchInfo->patchImageExportsArrayAddr);
+        const auto* clientArray = getAddrField<dyld_cache_image_clients_v2*>(patchInfo->patchClientsArrayAddr);
+        const auto* clientExports = getAddrField<dyld_cache_patchable_export_v2*>(patchInfo->patchClientExportsArrayAddr);
+        const auto* patchLocations = getAddrField<dyld_cache_patchable_location_v2*>(patchInfo->patchLocationArrayAddr);
+
+        // FIXME: This shouldn't be possible.  The cache builder always adds 1 dyld_cache_image_patches_v2 per-image.  But for now
+        // be conservative to match V1.
+        if (imageIndex > patchInfo->patchTableArrayCount)
+            return;
+
+        const dyld_cache_image_patches_v2& imagePatches = patchArray[imageIndex];
+        // FIXME: This shouldn't be possible.  We should catch this in the builder instead
+        if ( (imagePatches.patchClientsStartIndex + imagePatches.patchClientsCount) > patchInfo->patchClientsArrayCount )
+            return;
+
+        for (uint64_t clientIndex = 0; clientIndex != imagePatches.patchClientsCount; ++clientIndex) {
+            const dyld_cache_image_clients_v2& client = clientArray[imagePatches.patchClientsStartIndex + clientIndex];
+
+            // We only want fixups in a specific image.  Skip any others
+            if ( client.clientDylibIndex != userImageIndex )
+                continue;
+
+            // FIXME: This shouldn't be possible.  We should catch this in the builder instead
+            if ( (client.patchExportsStartIndex + client.patchExportsCount) > patchInfo->patchClientExportsArrayCount )
+                return;
+
+            for (uint64_t exportIndex = 0; exportIndex != client.patchExportsCount; ++exportIndex) {
+                const dyld_cache_patchable_export_v2& clientExport = clientExports[client.patchExportsStartIndex + exportIndex];
+
+                // The client export points to an image export from the image we are rooting
+                if ( clientExport.imageExportIndex > patchInfo->patchImageExportsArrayCount )
+                    return;
+
+                const dyld_cache_image_export_v2& imageExport = imageExports[clientExport.imageExportIndex];
+
+                if ( imageExport.dylibOffsetOfImpl != dylibVMOffsetOfImpl )
+                    continue;
+
+                if ( (clientExport.patchLocationsStartIndex + clientExport.patchLocationsCount) > patchInfo->patchLocationArrayCount )
+                    return;
+
+                for (uint64_t locationIndex = 0; locationIndex != clientExport.patchLocationsCount; ++locationIndex) {
+                    const dyld_cache_patchable_location_v2& patchLocation = patchLocations[clientExport.patchLocationsStartIndex + locationIndex];
+
+                    dyld3::MachOLoaded::PointerMetaData pmd;
+                    pmd.diversity         = patchLocation.discriminator;
+                    pmd.high8             = patchLocation.high7 << 1;
+                    pmd.authenticated     = patchLocation.authenticated;
+                    pmd.key               = patchLocation.key;
+                    pmd.usesAddrDiversity = patchLocation.usesAddressDiversity;
+
+                    handler(patchLocation.dylibOffsetOfUse, pmd, patchLocation.getAddend());
+                }
+            }
+
+            // We only wanted to process this image.  We are now done
+            break;
+        }
+        return;
+    }
+
+    assert(false);
 }
 
 void DyldSharedCache::forEachPatchableUseOfExport(uint32_t imageIndex, uint32_t dylibVMOffsetOfImpl,
                                                   void (^handler)(uint64_t cacheVMOffset,
-                                                                  MachOLoaded::PointerMetaData pmd, uint64_t addend,
-                                                                  bool isWeakImport)) const {
+                                                                  MachOLoaded::PointerMetaData pmd, uint64_t addend)) const {
     if ( header.patchInfoAddr == 0 )
         return;
 
     uint32_t patchVersion = patchInfoVersion();
-
-    // Get GOT patches if we have them
-    this->forEachPatchableGOTUseOfExport(imageIndex, dylibVMOffsetOfImpl, handler);
 
     if ( patchVersion == 1 ) {
         // Old cache.  The patch table uses the V1 structs
@@ -1448,53 +1559,95 @@
                 pmd.key               = patchLocation.key;
                 pmd.usesAddrDiversity = patchLocation.usesAddressDiversity;
 
-                handler(patchLocation.cacheOffset, pmd, patchLocation.getAddend(), false);
+                handler(patchLocation.cacheOffset, pmd, patchLocation.getAddend());
             }
         }
         return;
     }
 
-    // V2/V3 and newer structs
-    auto getDylibAddress = ^(uint32_t dylibImageIndex) {
-        auto* clientMF = (dyld3::MachOFile*)(this->getIndexedImageEntry(dylibImageIndex));
-        if ( clientMF == nullptr )
-            return 0ULL;
-        return clientMF->preferredLoadAddress();
-    };
-    PatchTable patchTable(this->patchTable(), header.patchInfoAddr);
-    patchTable.forEachPatchableCacheUseOfExport(imageIndex, dylibVMOffsetOfImpl,
-                                                this->unslidLoadAddress(),
-                                                getDylibAddress, handler);
-}
-
-void DyldSharedCache::forEachPatchableGOTUseOfExport(uint32_t imageIndex, uint32_t dylibVMOffsetOfImpl,
-                                                     void (^handler)(uint64_t cacheVMOffset,
-                                                                     MachOFile::PointerMetaData pmd,
-                                                                     uint64_t addend,
-                                                                     bool isWeakImport)) const {
-    if ( header.patchInfoAddr == 0 )
-        return;
-
-    uint32_t patchVersion = patchInfoVersion();
-
-    if ( patchVersion == 1 ) {
-        // Old cache.  Only V3 has GOT patching
-        return;
-    }
-
-    // V3 and newer structs
-    PatchTable patchTable(this->patchTable(), header.patchInfoAddr);
-    patchTable.forEachPatchableGOTUseOfExport(imageIndex, dylibVMOffsetOfImpl, handler);
+    // V2 and newer structs
+    if ( patchVersion == 2 ) {
+        const auto* patchInfo = getAddrField<dyld_cache_patch_info_v2*>(header.patchInfoAddr);
+        const auto* patchArray = getAddrField<dyld_cache_image_patches_v2*>(patchInfo->patchTableArrayAddr);
+        const auto* imageExports = getAddrField<dyld_cache_image_export_v2*>(patchInfo->patchImageExportsArrayAddr);
+        const auto* clientArray = getAddrField<dyld_cache_image_clients_v2*>(patchInfo->patchClientsArrayAddr);
+        const auto* clientExports = getAddrField<dyld_cache_patchable_export_v2*>(patchInfo->patchClientExportsArrayAddr);
+        const auto* patchLocations = getAddrField<dyld_cache_patchable_location_v2*>(patchInfo->patchLocationArrayAddr);
+
+        // FIXME: This shouldn't be possible.  The cache builder always adds 1 dyld_cache_image_patches_v2 per-image.  But for now
+        // be conservative to match V1.
+        if (imageIndex > patchInfo->patchTableArrayCount)
+            return;
+
+        const dyld_cache_image_patches_v2& imagePatches = patchArray[imageIndex];
+        // FIXME: This shouldn't be possible.  We should catch this in the builder instead
+        if ( (imagePatches.patchClientsStartIndex + imagePatches.patchClientsCount) > patchInfo->patchClientsArrayCount )
+            return;
+
+        uint64_t cacheUnslidAddress = unslidLoadAddress();
+
+        for (uint64_t clientIndex = 0; clientIndex != imagePatches.patchClientsCount; ++clientIndex) {
+            const dyld_cache_image_clients_v2& client = clientArray[imagePatches.patchClientsStartIndex + clientIndex];
+
+            // FIXME: This shouldn't be possible.  We should catch this in the builder instead
+            if ( (client.patchExportsStartIndex + client.patchExportsCount) > patchInfo->patchClientExportsArrayCount )
+                return;
+
+            // The callback wants a cache offset, so convert from "image + address" to a cache offset
+            uint64_t clientModTime;
+            uint64_t clientINode;
+            const dyld3::MachOAnalyzer* clientMA = (dyld3::MachOAnalyzer*)(this->getIndexedImageEntry(client.clientDylibIndex, clientModTime, clientINode));
+            if ( clientMA == nullptr )
+                return;
+
+            uint64_t clientUnslidAddress = clientMA->preferredLoadAddress();
+
+            for (uint64_t exportIndex = 0; exportIndex != client.patchExportsCount; ++exportIndex) {
+                const dyld_cache_patchable_export_v2& clientExport = clientExports[client.patchExportsStartIndex + exportIndex];
+
+                // The client export points to an image export from the image we are rooting
+                if ( clientExport.imageExportIndex > patchInfo->patchImageExportsArrayCount )
+                    return;
+
+                const dyld_cache_image_export_v2& imageExport = imageExports[clientExport.imageExportIndex];
+
+                if ( imageExport.dylibOffsetOfImpl != dylibVMOffsetOfImpl )
+                    continue;
+
+                if ( (clientExport.patchLocationsStartIndex + clientExport.patchLocationsCount) > patchInfo->patchLocationArrayCount )
+                    return;
+
+                for (uint64_t locationIndex = 0; locationIndex != clientExport.patchLocationsCount; ++locationIndex) {
+                    const dyld_cache_patchable_location_v2& patchLocation = patchLocations[clientExport.patchLocationsStartIndex + locationIndex];
+
+                    dyld3::MachOLoaded::PointerMetaData pmd;
+                    pmd.diversity         = patchLocation.discriminator;
+                    pmd.high8             = patchLocation.high7 << 1;
+                    pmd.authenticated     = patchLocation.authenticated;
+                    pmd.key               = patchLocation.key;
+                    pmd.usesAddrDiversity = patchLocation.usesAddressDiversity;
+
+                    uint64_t cacheOffset = (clientUnslidAddress + patchLocation.dylibOffsetOfUse) - cacheUnslidAddress;
+                    handler(cacheOffset, pmd, patchLocation.getAddend());
+                }
+            }
+        }
+        return;
+    }
+
+    assert(false);
 }
 
 #if !(BUILDING_LIBDYLD || BUILDING_DYLD)
 // MRM map file generator
-std::string DyldSharedCache::generateJSONMap(const char* disposition, uuid_t cache_uuid, bool verbose) const {
+std::string DyldSharedCache::generateJSONMap(const char* disposition) const {
     dyld3::json::Node cacheNode;
 
     cacheNode.map["version"].value = "1";
     cacheNode.map["disposition"].value = disposition;
     cacheNode.map["base-address"].value = dyld3::json::hex(unslidLoadAddress());
+    uuid_t cache_uuid;
+    getUUID(cache_uuid);
     uuid_string_t cache_uuidStr;
     uuid_unparse(cache_uuid, cache_uuidStr);
     cacheNode.map["uuid"].value = cache_uuidStr;
@@ -1517,21 +1670,6 @@
             segmentNode.map["name"].value = info.segName;
             segmentNode.map["start-vmaddr"].value = dyld3::json::hex(info.vmAddr);
             segmentNode.map["end-vmaddr"].value = dyld3::json::hex(info.vmAddr + info.vmSize);
-
-            // Add sections in verbose mode
-            if ( verbose ) {
-                __block dyld3::json::Node sectionsNode;
-                ma->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo& sectInfo, bool malformedSectionRange, bool& stopSection) {
-                    if ( sectInfo.segInfo.segIndex == info.segIndex ) {
-                        dyld3::json::Node sectionNode;
-                        sectionNode.map["name"].value = sectInfo.sectName;
-                        sectionNode.map["size"] = dyld3::json::Node(sectInfo.sectSize);
-                        sectionsNode.array.push_back(sectionNode);
-                    }
-                });
-                if ( !sectionsNode.array.empty() )
-                    segmentNode.map["sections"] = std::move(sectionsNode);
-            }
             segmentsNode.array.push_back(segmentNode);
         });
         imageNode.map["segments"] = segmentsNode;
@@ -1583,10 +1721,8 @@
     // until we find one with slide info
     forEachCache(^(const DyldSharedCache *cache, bool& stopCache) {
         cache->forEachSlideInfo(^(uint64_t mappingStartAddress, uint64_t mappingSize, const uint8_t *mappingPagesStart, uint64_t slideInfoOffset, uint64_t slideInfoSize, const dyld_cache_slide_info *slideInfoHeader) {
-            if ( slideInfoHeader->version == 1 ) {
-                pointerFormat   = VMAddrConverter::SharedCacheFormat::v1;
-                pointerValueAdd = 0;
-            } else if ( slideInfoHeader->version == 2 ) {
+            assert(slideInfoHeader->version >= 2);
+            if ( slideInfoHeader->version == 2 ) {
                 const dyld_cache_slide_info2* slideInfo = (dyld_cache_slide_info2*)(slideInfoHeader);
                 assert(slideInfo->delta_mask == 0x00FFFF0000000000);
                 pointerFormat   = VMAddrConverter::SharedCacheFormat::v2_x86_64_tbi;
@@ -1620,19 +1756,13 @@
 #endif
 
 bool DyldSharedCache::inDyldCache(const DyldSharedCache* cache, const dyld3::MachOFile* mf) {
+    // FIXME: The cache PBLS has a null state.config.dyldCache.addr when built, so we can't do the
+    // range check.  We might be able to remove the #if below if we can instead set the cache in the config.
+#if BUILDING_CACHE_BUILDER
+    return mf->inDyldCache();
+#else
     return mf->inDyldCache() && (cache != nullptr) && ((uintptr_t)mf >= (uintptr_t)cache) && ((uintptr_t)mf < ((uintptr_t)cache + cache->mappedSize()));
-}
-
-bool DyldSharedCache::isSubCachePath(const char* leafName)
-{
-    const char* firstDot = strchr(leafName, '.');
-    // check for files with a suffix, to know wether or not they are sub-caches
-    if ( firstDot != NULL ) {
-        // skip files that are not of the format "<baseName>.development", as they are sub-caches
-        if ( strcmp(firstDot, ".development") != 0 )
-            return true;
-    }
-    return false;
+#endif
 }
 
 #if !(BUILDING_LIBDYLD || BUILDING_DYLD)
@@ -1641,8 +1771,6 @@
                                            uint64_t baseCacheUnslidAddress,
                                            uint8_t* buffer)
 {
-    // We don't need to map R-X as we aren't running the code here, so only allow mapping up to RW
-    const uint32_t maxPermissions = VM_PROT_READ | VM_PROT_WRITE;
     struct stat statbuf;
     if ( ::stat(path, &statbuf) ) {
         fprintf(stderr, "Error: stat failed for dyld shared cache at %s\n", path);
@@ -1661,11 +1789,6 @@
         return nullptr;
     }
     const dyld_cache_header*       header   = (dyld_cache_header*)firstPage;
-    if ( strncmp(header->magic, "dyld_v1", 7) != 0 ) {
-        fprintf(stderr, "Error: Expected cache file magic to be 'dyld_v1...' in %s\n", path);
-        return nullptr;
-    }
-
    if ( header->mappingCount == 0 ) {
         fprintf(stderr, "Error: No mapping in shared cache file at %s\n", path);
         return nullptr;
@@ -1696,7 +1819,7 @@
     for (uint32_t i=0; i < header->mappingCount; ++i) {
         uint64_t mappingAddressOffset = mappings[i].address - mappings[0].address;
         void* mapped_cache = ::mmap((void*)(buffer + mappingAddressOffset + subCacheBufferOffset), (size_t)mappings[i].size,
-                                    mappings[i].maxProt & maxPermissions, MAP_FIXED | MAP_PRIVATE, cache_fd, mappings[i].fileOffset);
+                                    mappings[i].maxProt, MAP_FIXED | MAP_PRIVATE, cache_fd, mappings[i].fileOffset);
         if (mapped_cache == MAP_FAILED) {
             fprintf(stderr, "Error: mmap() for shared cache at %s failed, errno=%d\n", path, errno);
             return nullptr;
@@ -1716,33 +1839,20 @@
     std::vector<const DyldSharedCache*> caches;
     caches.push_back(cache);
 
-    std::string basePath = std::string(path);
-    if ( cache->header.cacheType == kDyldSharedCacheTypeUniversal )
-    {
-        std::size_t pos = basePath.find(DYLD_SHARED_CACHE_DEVELOPMENT_EXT);
-        if (pos != std::string::npos)
-            basePath = basePath.substr(0, basePath.size() - 12);
-    }
     // Load all subcaches, if we have them
     if ( cache->header.mappingOffset >= __offsetof(dyld_cache_header, subCacheArrayCount) ) {
         if ( cache->header.subCacheArrayCount != 0 ) {
             const dyld_subcache_entry* subCacheEntries = (dyld_subcache_entry*)((uint8_t*)cache + cache->header.subCacheArrayOffset);
-            bool hasCacheSuffix = cache->header.mappingOffset > __offsetof(dyld_cache_header, cacheSubType);
 
             for (uint32_t i = 0; i != cache->header.subCacheArrayCount; ++i) {
-                std::string subCachePath = std::string(path) + "." + dyld3::json::unpaddedDecimal(i + 1);
-                if ( hasCacheSuffix ) {
-                    subCachePath = basePath + subCacheEntries[i].fileSuffix;
-                }
+                std::string subCachePath = std::string(path) + "." + dyld3::json::decimal(i + 1);
                 const DyldSharedCache* subCache = DyldSharedCache::mapCacheFile(subCachePath.c_str(), cache->unslidLoadAddress(), (uint8_t*)cache);
                 if ( subCache == nullptr )
                     return {};
 
-                uint8_t uuid[16];
-                cache->getSubCacheUuid(i, uuid);
-                if ( memcmp(subCache->header.uuid, uuid, 16) != 0 ) {
+                if ( memcmp(subCache->header.uuid, subCacheEntries[i].uuid, 16) != 0 ) {
                     uuid_string_t expectedUUIDString;
-                    uuid_unparse_upper(uuid, expectedUUIDString);
+                    uuid_unparse_upper(subCacheEntries[i].uuid, expectedUUIDString);
                     uuid_string_t foundUUIDString;
                     uuid_unparse_upper(subCache->header.uuid, foundUUIDString);
                     fprintf(stderr, "Error: SubCache[%i] UUID mismatch.  Expected %s, got %s\n", i, expectedUUIDString, foundUUIDString);
@@ -1856,21 +1966,7 @@
     return (uint8_t*)(legacyCacheDataRegionMapping()->address) + slide;
 }
 
-const ObjCOptimizationHeader* DyldSharedCache::objcOpts() const
-{
-    if ( header.mappingOffset <= __offsetof(dyld_cache_header, objcOptsSize) )
-        return nullptr;
-
-    return (const ObjCOptimizationHeader*)((char*)this + header.objcOptsOffset);
-}
-
-#if BUILDING_CACHE_BUILDER
-const objc_opt::objc_opt_t* DyldSharedCache::oldObjcOpt() const
-{
-    return nullptr;
-}
-#else
-const objc_opt::objc_opt_t* DyldSharedCache::oldObjcOpt() const
+const objc_opt::objc_opt_t* DyldSharedCache::objcOpt() const
 {
     // Find the objc image
     __block const dyld3::MachOAnalyzer* objcMA = nullptr;
@@ -1879,6 +1975,15 @@
         uint64_t mTime;
         uint64_t inode;
         objcMA = (dyld3::MachOAnalyzer*)(this->getIndexedImageEntry(imageIndex, mTime, inode));
+    } else {
+#if BUILDING_CACHE_BUILDER
+        // The cache builder might call this before prior to the trie being written.
+        // In this case, we can still use the image list which is always available
+        forEachImage(^(const mach_header *mh, const char *installName) {
+            if ( !strcmp(installName, "/usr/lib/libobjc.A.dylib") )
+                objcMA = (const dyld3::MachOAnalyzer*)mh;
+        });
+#endif
     }
 
     if ( objcMA == nullptr )
@@ -1907,7 +2012,6 @@
 
     return nullptr;
 }
-#endif
 
 const void* DyldSharedCache::objcOptPtrs() const
 {
@@ -1946,109 +2050,31 @@
     return objcPointersContent;
 }
 
-bool DyldSharedCache::hasOptimizedObjC() const
-{
-    return (this->objcOpts() != nullptr) || (this->oldObjcOpt() != nullptr);
-}
-
-uint32_t DyldSharedCache::objcOptVersion() const
-{
-    if ( const ObjCOptimizationHeader* opts = this->objcOpts() )
-        return opts->version;
-    if ( const objc_opt::objc_opt_t* opts = this->oldObjcOpt() )
-        return opts->version;
-    return 0;
-}
-
-uint32_t DyldSharedCache::objcOptFlags() const
-{
-    if ( const ObjCOptimizationHeader* opts = this->objcOpts() )
-        return 0;
-    if ( const objc_opt::objc_opt_t* opts = this->oldObjcOpt() )
-        return opts->flags;
-    return 0;
-}
-
-const objc::HeaderInfoRO* DyldSharedCache::objcHeaderInfoRO() const
-{
-    if ( const ObjCOptimizationHeader* opts = this->objcOpts() ) {
-        if ( opts->headerInfoROCacheOffset != 0 )
-            return (const objc::HeaderInfoRO*)((char*)this + opts->headerInfoROCacheOffset);
-        return nullptr;
-    }
-    if ( const objc_opt::objc_opt_t* opts = this->oldObjcOpt() )
-        return (const objc::HeaderInfoRO*)opts->headeropt_ro();
-    return nullptr;
-}
-
-const objc::HeaderInfoRW* DyldSharedCache::objcHeaderInfoRW() const
-{
-    if ( const ObjCOptimizationHeader* opts = this->objcOpts() ) {
-        if ( opts->headerInfoRWCacheOffset != 0 )
-            return (const objc::HeaderInfoRW*)((char*)this + opts->headerInfoRWCacheOffset);
-        return nullptr;
-    }
-    if ( const objc_opt::objc_opt_t* opts = this->oldObjcOpt() )
-        return (const objc::HeaderInfoRW*)opts->headeropt_rw();
-    return nullptr;
-}
-
-const objc::SelectorHashTable* DyldSharedCache::objcSelectorHashTable() const
-{
-    if ( const ObjCOptimizationHeader* opts = this->objcOpts() ) {
-        if ( opts->selectorHashTableCacheOffset != 0 )
-            return (const objc::SelectorHashTable*)((char*)this + opts->selectorHashTableCacheOffset);
-        return nullptr;
-    }
-    if ( const objc_opt::objc_opt_t* opts = this->oldObjcOpt() )
-        return opts->selectorOpt();
-    return nullptr;
-}
-
-const objc::ClassHashTable* DyldSharedCache::objcClassHashTable() const
-{
-    if ( const ObjCOptimizationHeader* opts = this->objcOpts() ) {
-        if ( opts->classHashTableCacheOffset != 0 )
-            return (const objc::ClassHashTable*)((char*)this + opts->classHashTableCacheOffset);
-        return nullptr;
-    }
-    if ( const objc_opt::objc_opt_t* opts = this->oldObjcOpt() )
-        return opts->classOpt();
-    return nullptr;
-}
-
-const objc::ProtocolHashTable* DyldSharedCache::objcProtocolHashTable() const
-{
-    if ( const ObjCOptimizationHeader* opts = this->objcOpts() ) {
-        if ( opts->protocolHashTableCacheOffset != 0 )
-            return (const objc::ProtocolHashTable*)((char*)this + opts->protocolHashTableCacheOffset);
-        return nullptr;
-    }
-    if ( const objc_opt::objc_opt_t* opts = this->oldObjcOpt() )
-        return opts->protocolOpt();
-    return nullptr;
-}
-
-const void* DyldSharedCache::objcRelativeMethodListsBaseAddress() const
-{
-    if ( const ObjCOptimizationHeader* opts = this->objcOpts() ) {
-        if ( opts->relativeMethodSelectorBaseAddressOffset != 0 )
-            return (const void*)((char*)this + opts->relativeMethodSelectorBaseAddressOffset);
-        return nullptr;
-    }
-    if ( const objc_opt::objc_opt_t* opts = this->oldObjcOpt() )
-        return opts->relativeMethodListsBaseAddress();
-    return nullptr;
-}
-
 #if !(BUILDING_LIBDYLD || BUILDING_DYLD)
 uint64_t DyldSharedCache::sharedCacheRelativeSelectorBaseVMAddress() const {
-    const void* value = this->objcRelativeMethodListsBaseAddress();
-    if ( !value )
+    if ( header.mappingOffset <= __offsetof(dyld_cache_header, symbolFileUUID) )
         return 0;
 
-    uint64_t vmOffset = (uint64_t)value - (uint64_t)this;
-    return this->unslidLoadAddress() + vmOffset;
+    // In newer shared caches, relative method list selectors are offsets from the magic selector in libobjc
+    __block uint64_t sharedCacheRelativeSelectorBaseVMAddress = 0;
+    constexpr std::string_view magicSelector = "\xf0\x9f\xa4\xaf";
+    dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter = makeVMAddrConverter(false);
+    uint64_t sharedCacheSlide = (uint64_t)this - unslidLoadAddress();
+    forEachImage(^(const mach_header *mh, const char *installName) {
+        if ( !strcmp(installName, "/usr/lib/libobjc.A.dylib") ) {
+            const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)mh;
+            Diagnostics diag;
+            ma->forEachObjCSelectorReference(diag, vmAddrConverter,
+                                             ^(uint64_t selRefVMAddr, uint64_t selRefTargetVMAddr, bool &stop) {
+                const char* selValue = (const char*)(selRefTargetVMAddr + sharedCacheSlide);
+                if ( selValue == magicSelector ) {
+                    sharedCacheRelativeSelectorBaseVMAddress = selRefTargetVMAddr;
+                    stop = true;
+                }
+            });
+        }
+    });
+    return sharedCacheRelativeSelectorBaseVMAddress;
 }
 #endif
 
@@ -2207,5 +2233,3 @@
     });
 }
 #endif
-
-#endif // !TARGET_OS_EXCLAVEKIT