Loading...
common/CachePatching.cpp dyld-1335 dyld-1122.1
--- dyld/dyld-1335/common/CachePatching.cpp
+++ dyld/dyld-1122.1/common/CachePatching.cpp
@@ -28,7 +28,6 @@
 
 #if BUILDING_CACHE_BUILDER || BUILDING_CACHE_BUILDER_UNIT_TESTS
 #include "CacheDylib.h"
-#include <ranges>
 #endif // BUILDING_CACHE_BUILDER || BUILDING_CACHE_BUILDER_UNIT_TESTS
 
 #include <assert.h>
@@ -199,33 +198,21 @@
     }
 }
 
-void PatchTable::dump() const
-{
-#if BUILDING_SHARED_CACHE_UTIL
-    switch ( this->version() ) {
-        case 4:
-            return ((PatchTableV4*)this)->dump();
-        default:
-            assert("Unknown patch table version");
-            break;
-    }
-#endif
-}
-
 const char* PatchTable::patchKindName(PatchKind patchKind)
 {
+    const char* name = "(unknown patch kind)";
     switch ( patchKind ) {
         case PatchKind::regular:
-            return "";
+            name = "";
             break;
         case PatchKind::cfObj2:
-            return "(CF obj2)";
+            name = "(CF obj2)";
             break;
         case PatchKind::objcClass:
-            return "(objc class)";
-            break;
-    }
-    return "(unknown patch kind)";
+            name = "(objc class)";
+            break;
+    }
+    return name;
 }
 
 //
@@ -746,185 +733,6 @@
     }
 }
 
-void PatchTableV4::dump() const
-{
-#if BUILDING_SHARED_CACHE_UTIL
-
-    printf("version: %d\n", this->version());
-    printf("patch table address: 0x%llx\n", this->tableVMAddr);
-
-    {
-        const dyld_cache_patch_info_v4* patchInfo = this->info();
-        printf("header: {\n");
-        printf("  patchTableVersion: %d\n", patchInfo->patchTableVersion);
-        printf("  patchLocationVersion: %d\n", patchInfo->patchLocationVersion);
-        printf("  patchTableArrayAddr: %lld\n", patchInfo->patchTableArrayAddr);
-        printf("  patchTableArrayCount: %lld\n", patchInfo->patchTableArrayCount);
-        printf("  patchImageExportsArrayAddr: %lld\n", patchInfo->patchImageExportsArrayAddr);
-        printf("  patchImageExportsArrayCount: %lld\n", patchInfo->patchImageExportsArrayCount);
-        printf("  patchClientsArrayAddr: %lld\n", patchInfo->patchClientsArrayAddr);
-        printf("  patchClientsArrayCount: %lld\n", patchInfo->patchClientsArrayCount);
-        printf("  patchClientExportsArrayAddr: %lld\n", patchInfo->patchClientExportsArrayAddr);
-        printf("  patchClientExportsArrayCount: %lld\n", patchInfo->patchClientExportsArrayCount);
-        printf("  patchLocationArrayAddr: %lld\n", patchInfo->patchLocationArrayAddr);
-        printf("  patchLocationArrayCount: %lld\n", patchInfo->patchLocationArrayCount);
-        printf("  gotClientsArrayAddr: %lld\n", patchInfo->gotClientsArrayAddr);
-        printf("  gotClientsArrayCount: %lld\n", patchInfo->gotClientsArrayCount);
-        printf("  gotClientExportsArrayAddr: %lld\n", patchInfo->gotClientExportsArrayAddr);
-        printf("  gotClientExportsArrayCount: %lld\n", patchInfo->gotClientExportsArrayCount);
-        printf("  gotLocationArrayAddr: %lld\n", patchInfo->gotLocationArrayAddr);
-        printf("  gotLocationArrayCount: %lld\n", patchInfo->gotLocationArrayCount);
-        printf("  patchExportNamesAddr: %lld\n", patchInfo->patchExportNamesAddr);
-        printf("  patchExportNamesSize: %lld\n", patchInfo->patchExportNamesSize);
-        printf("}\n");
-    }
-
-    printf("\n");
-
-    {
-        std::span<const dyld_cache_image_patches_v2> elements = this->images();
-        printf("dyld_cache_image_patches_v2 (total = %zu): {\n", elements.size());
-        for ( const auto& element : elements ) {
-            printf("  [%ld]: { [%d, %d], [%d, %d] }\n",
-                   &element - elements.data(),
-                   element.patchClientsStartIndex, element.patchClientsCount,
-                   element.patchExportsStartIndex, element.patchExportsCount);
-        }
-        printf("} // dyld_cache_image_patches_v2\n");
-    }
-
-    printf("\n");
-
-    {
-        std::span<const dyld_cache_image_export_v2> elements = this->imageExports();
-        printf("dyld_cache_image_export_v2 (total = %zu): {\n", elements.size());
-        for ( const auto& element : elements ) {
-            printf("  [%ld]: { 0x%x, 0x%x, %d }\n",
-                   &element - elements.data(),
-                   element.dylibOffsetOfImpl, element.exportNameOffset,
-                   element.patchKind);
-        }
-        printf("} // dyld_cache_image_export_v2\n");
-    }
-
-    printf("\n");
-
-    {
-        std::span<const dyld_cache_image_clients_v2> elements = this->imageClients();
-        printf("dyld_cache_image_clients_v2 (total = %zu): {\n", elements.size());
-        for ( const auto& element : elements ) {
-            printf("  [%ld]: { %d, [%d, %d] }\n",
-                   &element - elements.data(),
-                   element.clientDylibIndex, element.patchExportsStartIndex,
-                   element.patchExportsCount);
-        }
-        printf("} // dyld_cache_image_clients_v2\n");
-    }
-
-    printf("\n");
-
-    {
-        std::span<const dyld_cache_patchable_export_v2> elements = this->clientExports();
-        printf("dyld_cache_patchable_export_v2 (total = %zu): {\n", elements.size());
-        for ( const auto& element : elements ) {
-            printf("  [%ld]: { %d, [%d, %d] }\n",
-                   &element - elements.data(),
-                   element.imageExportIndex, element.patchLocationsStartIndex,
-                   element.patchLocationsCount);
-        }
-        printf("} // dyld_cache_patchable_export_v2\n");
-    }
-
-    printf("\n");
-
-    {
-        std::span<const dyld_cache_patchable_location_v4> elements = this->patchableLocations();
-        printf("dyld_cache_patchable_location_v4 (total = %zu): {\n", elements.size());
-        for ( const auto& element : elements ) {
-            // TODO: regular vs auth
-            printf("  [%ld]: { 0x%x }\n",
-                   &element - elements.data(),
-                   element.dylibOffsetOfUse);
-        }
-        printf("} // dyld_cache_patchable_location_v4\n");
-    }
-
-    printf("\n");
-
-    {
-        std::span<const dyld_cache_image_got_clients_v3> elements = this->gotClients();
-        printf("dyld_cache_image_got_clients_v3 (total = %zu): {\n", elements.size());
-        for ( const auto& element : elements ) {
-            printf("  [%ld]: { [%d, %d] }\n",
-                   &element - elements.data(),
-                   element.patchExportsStartIndex, element.patchExportsCount);
-        }
-        printf("} // dyld_cache_image_got_clients_v3\n");
-    }
-
-    printf("\n");
-
-    {
-        std::span<const dyld_cache_patchable_export_v3> elements = this->gotClientExports();
-        printf("dyld_cache_patchable_export_v3 (total = %zu): {\n", elements.size());
-        for ( const auto& element : elements ) {
-            printf("  [%ld]: { %d, [%d, %d] }\n",
-                   &element - elements.data(),
-                   element.imageExportIndex,
-                   element.patchLocationsStartIndex, element.patchLocationsCount);
-        }
-        printf("} // dyld_cache_patchable_export_v3\n");
-    }
-
-    printf("\n");
-
-    {
-        std::span<const dyld_cache_patchable_location_v4_got> elements = this->gotPatchableLocations();
-        printf("dyld_cache_patchable_location_v4_got (total = %zu): {\n", elements.size());
-        for ( const auto& element : elements ) {
-            // TODO: regular vs auth
-            printf("  [%ld]: { 0x%llx }\n",
-                   &element - elements.data(),
-                   element.cacheOffsetOfUse);
-        }
-        printf("} // dyld_cache_patchable_location_v4_got\n");
-    }
-
-    printf("\n");
-
-    {
-        std::string_view elements = this->exportNames();
-        uint32_t totalStrings = 0;
-        for ( std::string_view str = elements; !str.empty(); ++totalStrings ) {
-            auto pos = str.find('\0');
-            if ( pos == std::string::npos )
-                break;
-            str = str.substr(pos + 1);
-        }
-
-        printf("export_names (total = %d): {\n", totalStrings);
-
-        uint32_t index = 0;
-        for ( std::string_view str = elements; !str.empty(); ++index ) {
-            auto pos = str.find('\0');
-            if ( pos == std::string::npos )
-                break;
-
-            printf("  [%d]: { 0x%lx, \"%s\" }\n",
-                   index,
-                   str.data() - elements.data(),
-                   str.data());
-
-            str = str.substr(pos + 1);
-        }
-        printf("} // export_names\n");
-    }
-
-    printf("\n");
-
-#endif // BUILDING_SHARED_CACHE_UTIL
-}
-
 #if BUILDING_CACHE_BUILDER || BUILDING_CACHE_BUILDER_UNIT_TESTS
 
 namespace cache_builder
@@ -938,9 +746,6 @@
                                         const std::span<PatchInfo>& patchInfos)
 {
     for ( const CacheDylib& cacheDylib : cacheDylibs ) {
-        if ( !cacheDylib.needsPatchTable )
-            continue;
-
         const PatchInfo& dylibPatchInfo = patchInfos[cacheDylib.cacheIndex];
         assert(cacheDylib.bindTargets.size() == dylibPatchInfo.bindUses.size());
         assert(cacheDylib.bindTargets.size() == dylibPatchInfo.bindTargetNames.size());
@@ -948,7 +753,7 @@
             const CacheDylib::BindTarget& bindTarget = cacheDylib.bindTargets[bindIndex];
 
             // Skip binds with no uses
-            const std::span<const DyldCachePatchableLocation> clientUses = dylibPatchInfo.bindUses[bindIndex];
+            const std::vector<dyld_cache_patchable_location>& clientUses = dylibPatchInfo.bindUses[bindIndex];
             if ( clientUses.empty() )
                 continue;
 
@@ -956,53 +761,40 @@
             if ( bindTarget.kind == CacheDylib::BindTarget::Kind::absolute )
                 continue;
 
-            assert(bindTarget.kind == CacheDylib::BindTarget::Kind::inputImage);
-            const CacheDylib::BindTarget::InputImage& inputImageTarget = bindTarget.inputImage;
-            InputDylibVMAddress                       bindTargetVMAddr = inputImageTarget.targetDylib->inputLoadAddress + inputImageTarget.targetRuntimeOffset;
-            assert(inputImageTarget.targetDylib->needsPatchTable && "target dylibs must be patchable");
+            assert(bindTarget.kind == CacheDylib::BindTarget::Kind::cacheImage);
+            const CacheDylib::BindTarget::CacheImage& cacheImageTarget = bindTarget.cacheImage;
+            CacheVMAddress                            bindTargetVMAddr = cacheImageTarget.targetDylib->cacheLoadAddress + cacheImageTarget.targetRuntimeOffset;
 
             // Find the target dylib.  We need to add this dylib as a client of the target
-            DylibClients& targetDylibClients = dylibClients[inputImageTarget.targetDylib->cacheIndex];
+            DylibClients& targetDylibClients = dylibClients[cacheImageTarget.targetDylib->cacheIndex];
 
             // Add this dylib as a client if its not already there
             if ( targetDylibClients.clients.empty() || (targetDylibClients.clients.back().clientCacheDylib != &cacheDylib) )
                 targetDylibClients.clients.emplace_back(&cacheDylib);
 
             DylibClient&                                targetDylibClient = targetDylibClients.clients.back();
-            std::vector<DyldCachePatchableLocation>&    uses              = targetDylibClient.uses[bindTargetVMAddr];
+            std::vector<dyld_cache_patchable_location>& uses              = targetDylibClient.uses[bindTargetVMAddr];
             uses.insert(uses.end(), clientUses.begin(), clientUses.end());
 
-            targetDylibClients.exportsToName.insert({ bindTargetVMAddr, dylibPatchInfo.bindTargetNames[bindIndex] });
+            exportsToName.insert({ bindTargetVMAddr, dylibPatchInfo.bindTargetNames[bindIndex] });
 
             if ( log ) {
                 printf("%d patch loc(s) in %s, of symbol %s in %s\n",
                        (uint32_t)clientUses.size(), cacheDylib.installName.data(),
                        dylibPatchInfo.bindTargetNames[bindIndex].c_str(),
-                       cacheDylibs[inputImageTarget.targetDylib->cacheIndex].installName.data());
+                       cacheDylibs[cacheImageTarget.targetDylib->cacheIndex].installName.data());
             }
         }
 
         // GOTs
-        for ( UniquedGOTKind sectionKind : { UniquedGOTKind::regular, UniquedGOTKind::authGot, UniquedGOTKind::authPtr } ) {
-            std::span<const std::vector<PatchInfo::GOTInfo>> bindGOTUses;
-            switch ( sectionKind ) {
-                case UniquedGOTKind::regular:
-                    bindGOTUses = dylibPatchInfo.bindGOTUses;
-                    break;
-                case UniquedGOTKind::authGot:
-                    bindGOTUses = dylibPatchInfo.bindAuthGOTUses;
-                    break;
-                case UniquedGOTKind::authPtr:
-                    bindGOTUses = dylibPatchInfo.bindAuthPtrUses;
-                    break;
-            }
-
+        for ( bool auth : { false, true } ) {
+            const auto& bindGOTUses = auth ? dylibPatchInfo.bindAuthGOTUses : dylibPatchInfo.bindGOTUses;
             assert(cacheDylib.bindTargets.size() == bindGOTUses.size());
             for ( uint32_t bindIndex = 0; bindIndex != cacheDylib.bindTargets.size(); ++bindIndex ) {
                 const CacheDylib::BindTarget& bindTarget = cacheDylib.bindTargets[bindIndex];
 
                 // Skip binds with no uses
-                const std::span<const PatchInfo::GOTInfo> clientUses = bindGOTUses[bindIndex];
+                const std::vector<PatchInfo::GOTInfo>& clientUses = bindGOTUses[bindIndex];
                 if ( clientUses.empty() )
                     continue;
 
@@ -1010,25 +802,25 @@
                 if ( bindTarget.kind == CacheDylib::BindTarget::Kind::absolute )
                     continue;
 
-                assert(bindTarget.kind == CacheDylib::BindTarget::Kind::inputImage);
-                const CacheDylib::BindTarget::InputImage& inputImageTarget = bindTarget.inputImage;
-                InputDylibVMAddress                       bindTargetVMAddr = inputImageTarget.targetDylib->inputLoadAddress + inputImageTarget.targetRuntimeOffset;
+                assert(bindTarget.kind == CacheDylib::BindTarget::Kind::cacheImage);
+                const CacheDylib::BindTarget::CacheImage& cacheImageTarget = bindTarget.cacheImage;
+                CacheVMAddress                            bindTargetVMAddr = cacheImageTarget.targetDylib->cacheLoadAddress + cacheImageTarget.targetRuntimeOffset;
 
                 // Find the target dylib.  We need to add this dylib as a client of the target
-                DylibClients& targetDylibClients = dylibClients[inputImageTarget.targetDylib->cacheIndex];
-
-                GOTClient&                                  gotClient = targetDylibClients.gotClient;
-                std::vector<DyldCachePatchableGOTLocation>& uses      = gotClient.uses[bindTargetVMAddr];
+                DylibClients& targetDylibClients = dylibClients[cacheImageTarget.targetDylib->cacheIndex];
+
+                DylibClient&                                targetDylibClient = targetDylibClients.gotClient;
+                std::vector<dyld_cache_patchable_location>& uses              = targetDylibClient.uses[bindTargetVMAddr];
                 for ( const PatchInfo::GOTInfo& gotInfo : clientUses )
-                    uses.push_back(gotInfo.useLocation);
-
-                targetDylibClients.exportsToName.insert({ bindTargetVMAddr, dylibPatchInfo.bindTargetNames[bindIndex] });
+                    uses.push_back(gotInfo.patchInfo);
+
+                exportsToName.insert({ bindTargetVMAddr, dylibPatchInfo.bindTargetNames[bindIndex] });
 
                 if ( log ) {
                     printf("%d patch loc(s) in %s, of symbol %s in %s\n",
                            (uint32_t)clientUses.size(), cacheDylib.installName.data(),
                            dylibPatchInfo.bindTargetNames[bindIndex].c_str(),
-                           cacheDylibs[inputImageTarget.targetDylib->cacheIndex].installName.data());
+                           cacheDylibs[cacheImageTarget.targetDylib->cacheIndex].installName.data());
                 }
             }
         }
@@ -1048,18 +840,18 @@
     uint64_t numGotClientExports     = 0;
     uint64_t numGotPatchLocations    = 0;
 
+    typedef std::unordered_map<CacheVMAddress, uint32_t, CacheVMAddressHash, CacheVMAddressEqual> ExportNameOffsetMap;
+    ExportNameOffsetMap exportNameOffsets;
+
     for ( uint32_t dylibIndex = 0; dylibIndex != cacheDylibs.size(); ++dylibIndex ) {
         DylibClients& dylibClientData = dylibClients[dylibIndex];
-        std::vector<InputDylibVMAddress> usedExports;
-
-        typedef std::unordered_map<InputDylibVMAddress, uint32_t, InputDylibVMAddressHash, InputDylibVMAddressEqual> ExportNameOffsetMap;
-        ExportNameOffsetMap exportNameOffsets;
+        std::vector<CacheVMAddress> usedExports;
 
         for ( const DylibClient& clientDylib : dylibClientData.clients ) {
             bool clientUsed = false;
-            for ( auto& exportDylibVMAddrAndUses : clientDylib.uses ) {
-                InputDylibVMAddress                               exportDylibVMAddr = exportDylibVMAddrAndUses.first;
-                const std::span<const DyldCachePatchableLocation> uses              = exportDylibVMAddrAndUses.second;
+            for ( auto& exportVMAddrAndUses : clientDylib.uses ) {
+                CacheVMAddress                                    exportCacheVMAddr = exportVMAddrAndUses.first;
+                const std::vector<dyld_cache_patchable_location>& uses              = exportVMAddrAndUses.second;
                 if ( uses.empty() )
                     continue;
 
@@ -1069,15 +861,15 @@
                 numPatchLocations += uses.size();
 
                 // Track this location as one the target dylib needs to export
-                usedExports.push_back(exportDylibVMAddr);
+                usedExports.push_back(exportCacheVMAddr);
 
                 // We need space for the name too
-                auto itAndInserted = exportNameOffsets.insert({ exportDylibVMAddr, numPatchExportNameBytes });
+                auto itAndInserted = exportNameOffsets.insert({ exportCacheVMAddr, numPatchExportNameBytes });
                 if ( itAndInserted.second ) {
                     // We inserted the name, so make space for it
                     // We should have an export already, from the previous scan to size the tables
-                    auto exportNameIt = dylibClientData.exportsToName.find(exportDylibVMAddr);
-                    assert(exportNameIt != dylibClientData.exportsToName.end());
+                    auto exportNameIt = exportsToName.find(exportCacheVMAddr);
+                    assert(exportNameIt != exportsToName.end());
                     const std::string_view& exportName = exportNameIt->second;
                     numPatchExportNameBytes += exportName.size() + 1;
                 }
@@ -1090,10 +882,9 @@
 
         // GOTs
         {
-            for ( auto& exportDylibVMAddrAndUses : dylibClientData.gotClient.uses ) {
-                InputDylibVMAddress                         exportDylibVMAddr = exportDylibVMAddrAndUses.first;
-                std::vector<DyldCachePatchableGOTLocation>& uses              = exportDylibVMAddrAndUses.second;
-
+            for ( auto& exportVMAddrAndUses : dylibClientData.gotClient.uses ) {
+                CacheVMAddress                              exportCacheVMAddr = exportVMAddrAndUses.first;
+                std::vector<dyld_cache_patchable_location>& uses              = exportVMAddrAndUses.second;
                 if ( uses.empty() )
                     continue;
 
@@ -1105,15 +896,15 @@
                 numGotPatchLocations += uses.size();
 
                 // Track this location as one the target dylib needs to export
-                usedExports.push_back(exportDylibVMAddr);
+                usedExports.push_back(exportCacheVMAddr);
 
                 // We need space for the name too
-                auto itAndInserted = exportNameOffsets.insert({ exportDylibVMAddr, numPatchExportNameBytes });
+                auto itAndInserted = exportNameOffsets.insert({ exportCacheVMAddr, numPatchExportNameBytes });
                 if ( itAndInserted.second ) {
                     // We inserted the name, so make space for it
                     // We should have an export already, from the previous scan to size the tables
-                    auto exportNameIt = dylibClientData.exportsToName.find(exportDylibVMAddr);
-                    assert(exportNameIt != dylibClientData.exportsToName.end());
+                    auto exportNameIt = exportsToName.find(exportCacheVMAddr);
+                    assert(exportNameIt != exportsToName.end());
                     const std::string_view& exportName = exportNameIt->second;
                     numPatchExportNameBytes += exportName.size() + 1;
                 }
@@ -1126,50 +917,20 @@
         dylibClientData.setUsedExports(std::move(usedExports));
 
         // Track how many exports this image needs
-        numImageExports += dylibClientData.getUsedInputDylibExports().size();
-    }
-
-    // align to 4
-    while ( (numPatchExportNameBytes % 4) != 0 )
-        ++numPatchExportNameBytes;
+        numImageExports += dylibClientData.getUsedExports().size();
+    }
 
     // Now reserve the space
+
     patchImages.reserve(numPatchImages);
     imageExports.reserve(numImageExports);
     patchClients.reserve(numPatchClients);
     clientExports.reserve(numClientExports);
     patchLocations.reserve(numPatchLocations);
+    patchExportNames.reserve(numPatchExportNameBytes);
     gotClients.reserve(numGOTClients);
     gotClientExports.reserve(numGotClientExports);
     gotPatchLocations.reserve(numGotPatchLocations);
-    patchExportNames.reserve(numPatchExportNameBytes);
-
-    uint64_t patchInfoSize = sizeof(dyld_cache_patch_info_v4);
-    patchInfoSize += sizeof(dyld_cache_image_patches_v2) * numPatchImages;
-    patchInfoSize += sizeof(dyld_cache_image_export_v2) * numImageExports;
-    patchInfoSize += sizeof(dyld_cache_image_clients_v2) * numPatchClients;
-    patchInfoSize += sizeof(dyld_cache_patchable_export_v3) * numClientExports;
-    patchInfoSize += sizeof(dyld_cache_patchable_location_v4) * numPatchLocations;
-    patchInfoSize += sizeof(dyld_cache_image_got_clients_v3) * numGOTClients;
-    patchInfoSize += sizeof(dyld_cache_patchable_export_v3) * numGotClientExports;
-    patchInfoSize += sizeof(dyld_cache_patchable_location_v4_got) * numGotPatchLocations;
-    patchInfoSize += numPatchExportNameBytes;
-
-    totalSize = patchInfoSize;
-
-#if 0
-    fprintf(stderr, "sizeof(dyld_cache_patch_info_v3): %lu\n", sizeof(dyld_cache_patch_info_v3));
-    fprintf(stderr, "sizeof(dyld_cache_image_patches_v2) * patchImages.size(): %lu with %lu uses\n", sizeof(dyld_cache_image_patches_v2) * patchImages.size(), patchImages.size());
-    fprintf(stderr, "sizeof(dyld_cache_image_export_v2) * imageExports.size(): %lu with %lu uses\n", sizeof(dyld_cache_image_export_v2) * imageExports.size(), imageExports.size());
-    fprintf(stderr, "sizeof(dyld_cache_image_clients_v2) * patchClients.size(): %lu with %lu uses\n", sizeof(dyld_cache_image_clients_v2) * patchClients.size(), patchClients.size());
-    fprintf(stderr, "sizeof(dyld_cache_patchable_export_v2) * clientExports.size(): %lu with %lu uses\n", sizeof(dyld_cache_patchable_export_v2) * clientExports.size(), clientExports.size());
-    fprintf(stderr, "sizeof(dyld_cache_patchable_location_v2) * patchLocations.size(): %lu with %lu uses\n", sizeof(dyld_cache_patchable_location_v2) * patchLocations.size(), patchLocations.size());
-    fprintf(stderr, "sizeof(dyld_cache_image_got_clients_v3) * gotClients.size(): %lu with %lu uses\n", sizeof(dyld_cache_image_got_clients_v3) * gotClients.size(), gotClients.size());
-    fprintf(stderr, "sizeof(dyld_cache_patchable_export_v3) * gotClientExports.size(): %lu with %lu uses\n", sizeof(dyld_cache_patchable_export_v3) * gotClientExports.size(), gotClientExports.size());
-    fprintf(stderr, "sizeof(dyld_cache_patchable_location_v3) * gotPatchLocations.size(): %lu with %lu uses\n", sizeof(dyld_cache_patchable_location_v3) * gotPatchLocations.size(), gotPatchLocations.size());
-    fprintf(stderr, "patchExportNames.size(): %lu\n", patchExportNames.size());
-    fprintf(stderr, "patchInfoSize: %lld\n", patchInfoSize);
-#endif
 }
 
 void PatchTableBuilder::calculatePatchTable(const std::span<CacheDylib>& cacheDylibs,
@@ -1177,13 +938,6 @@
                                             const PatchableSingletonsSet& patchableCFObj2,
                                             CacheVMAddress cacheBaseAddress)
 {
-    // Calculate the real VM addresses for all exports.  This needs to be done late as the input dylib addresses
-    // can change sort order when the cache moves segments
-    for ( uint32_t dylibIndex = 0; dylibIndex != cacheDylibs.size(); ++dylibIndex ) {
-        DylibClients& dylibClientData = dylibClients[dylibIndex];
-        dylibClientData.calculateCacheUsedExports(cacheDylibs[dylibIndex]);
-    }
-
     typedef std::unordered_map<CacheVMAddress, uint32_t, CacheVMAddressHash, CacheVMAddressEqual> ExportNameOffsetMap;
     ExportNameOffsetMap exportNameOffsets;
 
@@ -1197,27 +951,13 @@
         patchImage.patchClientsStartIndex = (uint32_t)patchClients.size();
         patchImage.patchClientsCount      = 0;
         patchImage.patchExportsStartIndex = (uint32_t)imageExports.size();
-        patchImage.patchExportsCount      = (uint32_t)dylibClientData.getUsedCacheDylibExports().size();
-
-        if ( !cacheDylibs[dylibIndex].needsPatchTable ) {
-            assert(patchImage.patchExportsCount == 0);
-            assert(dylibClientData.clients.empty());
-            patchImages.push_back(patchImage);
-
-            // got clients entry needed even if unused
-            dyld_cache_image_got_clients_v3 gotClient;
-            gotClient.patchExportsStartIndex   = (uint32_t)gotClientExports.size();
-            gotClient.patchExportsCount        = 0;
-            gotClients.push_back(gotClient);
-            continue;
-        }
+        patchImage.patchExportsCount      = (uint32_t)dylibClientData.getUsedExports().size();
 
         // Add regular clients
         for ( const DylibClient& clientDylib : dylibClientData.clients ) {
             bool clientUsed = false;
 
-            InputDylibVMAddress clientInputDylibVMAddr = clientDylib.clientCacheDylib->inputLoadAddress;
-            CacheVMAddress clientCacheDylibVMAddr = clientDylib.clientCacheDylib->cacheLoadAddress;
+            CacheVMAddress clientDylibVMAddr = clientDylib.clientCacheDylib->cacheLoadAddress;
 
             // We might add a client.  If we do, then set it up now so that we have the
             // right offset to the exports table
@@ -1226,19 +966,10 @@
             clientImage.patchExportsStartIndex = (uint32_t)clientExports.size();
             clientImage.patchExportsCount      = 0;
 
-            // Make a sorted map of cache addresses so that code in dyld at runtime can do binary search
-            std::map<CacheVMAddress, InputDylibVMAddress, CacheVMAddressLessThan> exportMap;
             for ( auto& exportVMAddrAndUses : clientDylib.uses ) {
-                InputDylibVMAddress exportInputDylibVMAddr = exportVMAddrAndUses.first;
-                CacheVMAddress exportCacheDylibVMAddr = cacheDylibs[dylibIndex].adjustor->adjustVMAddr(exportInputDylibVMAddr);
-                exportMap[exportCacheDylibVMAddr] = exportInputDylibVMAddr;
-            }
-
-            for ( auto& cacheAndInputAddress : exportMap ) {
-                const CacheVMAddress exportCacheDylibVMAddr = cacheAndInputAddress.first;
-                const InputDylibVMAddress exportInputDylibVMAddr = cacheAndInputAddress.second;
-
-                const std::span<const DyldCachePatchableLocation> uses = clientDylib.uses.at(exportInputDylibVMAddr);
+                CacheVMAddress exportCacheVMAddr = exportVMAddrAndUses.first;
+
+                const std::vector<dyld_cache_patchable_location>& uses = exportVMAddrAndUses.second;
                 if ( uses.empty() )
                     continue;
 
@@ -1246,7 +977,10 @@
                 clientUsed = true;
 
                 // We should have an export already, from the previous scan to size the tables
-                uint32_t imageExportIndex = dylibClientData.findExportIndex(exportCacheDylibVMAddr);
+                auto exportIt = dylibClientData.findExport(exportCacheVMAddr);
+                assert(exportIt != dylibClientData.getUsedExports().end());
+
+                uint32_t imageExportIndex = (uint32_t)std::distance(dylibClientData.getUsedExports().cbegin(), exportIt);
 
                 // Add an export for this client dylib
                 dyld_cache_patchable_export_v2 cacheExport;
@@ -1258,12 +992,9 @@
 
                 // Now add the list of locations.
                 // At this point we need to translate from the locations the cache recorded to what we encode
-                for ( const DyldCachePatchableLocation& use : uses ) {
+                for ( const dyld_cache_patchable_location& use : uses ) {
                     dyld_cache_patchable_location_v4 loc;
-                    // Translate from the offset in the input dylib to an offset in the final dylib
-                    InputDylibVMAddress inputVMAddr = clientInputDylibVMAddr + use.clientDylibOffset;
-                    CacheVMAddress cacheVMAddr = clientDylib.clientCacheDylib->adjustor->adjustVMAddr(inputVMAddr);
-                    loc.dylibOffsetOfUse     = (uint32_t)(cacheVMAddr - clientCacheDylibVMAddr).rawValue();
+                    loc.dylibOffsetOfUse     = (uint32_t)(use.cacheVMAddr - clientDylibVMAddr).rawValue();
                     if ( use.authenticated ) {
                         loc.auth.high7                = use.high7;
                         loc.auth.isWeakImport         = use.isWeakImport;
@@ -1295,25 +1026,18 @@
             gotClient.patchExportsStartIndex   = (uint32_t)gotClientExports.size();
             gotClient.patchExportsCount        = 0;
 
-            // Make a sorted map of cache addresses so that code in dyld at runtime can do binary search
-            std::map<CacheVMAddress, InputDylibVMAddress, CacheVMAddressLessThan> exportMap;
             for ( auto& exportVMAddrAndUses : dylibClientData.gotClient.uses ) {
-                InputDylibVMAddress exportInputDylibVMAddr = exportVMAddrAndUses.first;
-                CacheVMAddress exportCacheDylibVMAddr = cacheDylibs[dylibIndex].adjustor->adjustVMAddr(exportInputDylibVMAddr);
-                exportMap[exportCacheDylibVMAddr] = exportInputDylibVMAddr;
-            }
-
-            for ( auto& cacheAndInputAddress : exportMap ) {
-                const CacheVMAddress exportCacheDylibVMAddr = cacheAndInputAddress.first;
-                const InputDylibVMAddress exportInputDylibVMAddr = cacheAndInputAddress.second;
-
-                const std::span<const DyldCachePatchableGOTLocation> uses = dylibClientData.gotClient.uses.at(exportInputDylibVMAddr);
-
+                CacheVMAddress exportCacheVMAddr = exportVMAddrAndUses.first;
+
+                const std::vector<dyld_cache_patchable_location>& uses = exportVMAddrAndUses.second;
                 if ( uses.empty() )
                     continue;
 
                 // We should have an export already, from the previous scan to size the tables
-                uint32_t imageExportIndex = dylibClientData.findExportIndex(exportCacheDylibVMAddr);
+                auto exportIt = dylibClientData.findExport(exportCacheVMAddr);
+                assert(exportIt != dylibClientData.getUsedExports().end());
+
+                uint32_t imageExportIndex = (uint32_t)std::distance(dylibClientData.getUsedExports().cbegin(), exportIt);
 
                 // Add an export for this GOT client
                 dyld_cache_patchable_export_v3 cacheExport;
@@ -1325,11 +1049,9 @@
 
                 // Now add the list of locations.
                 // At this point we need to translate from the locations the cache recorded to what we encode
-                for (const DyldCachePatchableGOTLocation& use : uses) {
+                for (const dyld_cache_patchable_location& use : uses) {
                     dyld_cache_patchable_location_v4_got loc;
-                    CacheVMAddress cacheVMAddr      = use.clientGOT->cacheVMAddress + use.clientGOTOffset;
-                    loc.cacheOffsetOfUse            = (cacheVMAddr - cacheBaseAddress).rawValue();
-                    loc.unusedPadding               = 0;
+                    loc.cacheOffsetOfUse            = (use.cacheVMAddr - cacheBaseAddress).rawValue();
                     if ( use.authenticated ) {
                         loc.auth.high7                = use.high7;
                         loc.auth.isWeakImport         = use.isWeakImport;
@@ -1356,16 +1078,13 @@
         CacheVMAddress    imageBaseAddress = cacheDylib.cacheLoadAddress;
 
         // Add all the exports for this image
-        for ( const auto& cacheAndInputAddress : dylibClientData.getUsedCacheDylibExports() ) {
-            const CacheVMAddress exportCacheDylibVMAddr = cacheAndInputAddress.cacheAddr;
-            const InputDylibVMAddress exportInputDylibVMAddr = cacheAndInputAddress.inputAddr;
-
+        for ( const CacheVMAddress exportCacheVMAddr : dylibClientData.getUsedExports() ) {
             // Add the name, if no-one else has
             uint32_t exportNameOffset  = 0;
-            auto     nameItAndInserted = exportNameOffsets.insert({ exportCacheDylibVMAddr, (uint32_t)patchExportNames.size() });
+            auto     nameItAndInserted = exportNameOffsets.insert({ exportCacheVMAddr, (uint32_t)patchExportNames.size() });
             if ( nameItAndInserted.second ) {
                 // We inserted the name, so make space for it
-                const std::string_view& exportName = dylibClientData.exportsToName[exportInputDylibVMAddr];
+                const std::string_view& exportName = exportsToName[exportCacheVMAddr];
                 patchExportNames.insert(patchExportNames.end(), &exportName[0], &exportName[0] + exportName.size() + 1);
                 exportNameOffset = nameItAndInserted.first->second;
             }
@@ -1374,16 +1093,17 @@
                 exportNameOffset = nameItAndInserted.first->second;
             }
 
+
             PatchKind patchKind = PatchKind::regular;
-            if ( patchableObjCClasses.count(exportCacheDylibVMAddr) ) {
+            if ( patchableObjCClasses.count(exportCacheVMAddr) ) {
                 patchKind = PatchKind::objcClass;
             }
-            else if ( patchableCFObj2.count(exportCacheDylibVMAddr) ) {
+            else if ( patchableCFObj2.count(exportCacheVMAddr) ) {
                 patchKind = PatchKind::cfObj2;
             }
 
             dyld_cache_image_export_v2 imageExport;
-            imageExport.dylibOffsetOfImpl = (uint32_t)(exportCacheDylibVMAddr - imageBaseAddress).rawValue();
+            imageExport.dylibOffsetOfImpl = (uint32_t)(exportCacheVMAddr - imageBaseAddress).rawValue();
             imageExport.exportNameOffset  = (uint32_t)exportNameOffset;
             imageExport.patchKind         = (uint32_t)patchKind;
             imageExports.push_back(imageExport);
@@ -1398,7 +1118,32 @@
 
 uint64_t PatchTableBuilder::getPatchTableSize() const
 {
-    return totalSize;
+    uint64_t patchInfoSize = sizeof(dyld_cache_patch_info_v3);
+    patchInfoSize += sizeof(dyld_cache_image_patches_v2) * patchImages.size();
+    patchInfoSize += sizeof(dyld_cache_image_export_v2) * imageExports.size();
+    patchInfoSize += sizeof(dyld_cache_image_clients_v2) * patchClients.size();
+    patchInfoSize += sizeof(dyld_cache_patchable_export_v2) * clientExports.size();
+    patchInfoSize += sizeof(dyld_cache_patchable_location_v2) * patchLocations.size();
+    patchInfoSize += sizeof(dyld_cache_image_got_clients_v3) * gotClients.size();
+    patchInfoSize += sizeof(dyld_cache_patchable_export_v3) * gotClientExports.size();
+    patchInfoSize += sizeof(dyld_cache_patchable_location_v3) * gotPatchLocations.size();
+    patchInfoSize += patchExportNames.size();
+    
+#if 0
+    fprintf(stderr, "sizeof(dyld_cache_patch_info_v3): %lu\n", sizeof(dyld_cache_patch_info_v3));
+    fprintf(stderr, "sizeof(dyld_cache_image_patches_v2) * patchImages.size(): %lu with %lu uses\n", sizeof(dyld_cache_image_patches_v2) * patchImages.size(), patchImages.size());
+    fprintf(stderr, "sizeof(dyld_cache_image_export_v2) * imageExports.size(): %lu with %lu uses\n", sizeof(dyld_cache_image_export_v2) * imageExports.size(), imageExports.size());
+    fprintf(stderr, "sizeof(dyld_cache_image_clients_v2) * patchClients.size(): %lu with %lu uses\n", sizeof(dyld_cache_image_clients_v2) * patchClients.size(), patchClients.size());
+    fprintf(stderr, "sizeof(dyld_cache_patchable_export_v2) * clientExports.size(): %lu with %lu uses\n", sizeof(dyld_cache_patchable_export_v2) * clientExports.size(), clientExports.size());
+    fprintf(stderr, "sizeof(dyld_cache_patchable_location_v2) * patchLocations.size(): %lu with %lu uses\n", sizeof(dyld_cache_patchable_location_v2) * patchLocations.size(), patchLocations.size());
+    fprintf(stderr, "sizeof(dyld_cache_image_got_clients_v3) * gotClients.size(): %lu with %lu uses\n", sizeof(dyld_cache_image_got_clients_v3) * gotClients.size(), gotClients.size());
+    fprintf(stderr, "sizeof(dyld_cache_patchable_export_v3) * gotClientExports.size(): %lu with %lu uses\n", sizeof(dyld_cache_patchable_export_v3) * gotClientExports.size(), gotClientExports.size());
+    fprintf(stderr, "sizeof(dyld_cache_patchable_location_v3) * gotPatchLocations.size(): %lu with %lu uses\n", sizeof(dyld_cache_patchable_location_v3) * gotPatchLocations.size(), gotPatchLocations.size());
+    fprintf(stderr, "patchExportNames.size(): %lu\n", patchExportNames.size());
+    fprintf(stderr, "patchInfoSize: %lld\n", patchInfoSize);
+#endif
+    
+    return patchInfoSize;
 }
 
 Error PatchTableBuilder::write(uint8_t* buffer, uint64_t bufferSize,
@@ -1435,49 +1180,20 @@
 
     // (dylib, client) patch table
     ::memcpy(buffer + patchInfoAddr - patchInfoAddr, &patchInfo, sizeof(dyld_cache_patch_info_v3));
-    if ( !patchImages.empty() )
-        ::memcpy(buffer + patchInfo.patchTableArrayAddr - patchInfoAddr, &patchImages[0], sizeof(patchImages[0]) * patchImages.size());
-    if ( !imageExports.empty() )
-        ::memcpy(buffer + patchInfo.patchImageExportsArrayAddr - patchInfoAddr, &imageExports[0], sizeof(imageExports[0]) * imageExports.size());
-    if ( !patchClients.empty() )
-        ::memcpy(buffer + patchInfo.patchClientsArrayAddr - patchInfoAddr, &patchClients[0], sizeof(patchClients[0]) * patchClients.size());
-    if ( !clientExports.empty() )
-        ::memcpy(buffer + patchInfo.patchClientExportsArrayAddr - patchInfoAddr, &clientExports[0], sizeof(clientExports[0]) * clientExports.size());
-    if ( !patchLocations.empty() )
-        ::memcpy(buffer + patchInfo.patchLocationArrayAddr - patchInfoAddr, &patchLocations[0], sizeof(patchLocations[0]) * patchLocations.size());
+    ::memcpy(buffer + patchInfo.patchTableArrayAddr - patchInfoAddr, &patchImages[0], sizeof(patchImages[0]) * patchImages.size());
+    ::memcpy(buffer + patchInfo.patchImageExportsArrayAddr - patchInfoAddr, &imageExports[0], sizeof(imageExports[0]) * imageExports.size());
+    ::memcpy(buffer + patchInfo.patchClientsArrayAddr - patchInfoAddr, &patchClients[0], sizeof(patchClients[0]) * patchClients.size());
+    ::memcpy(buffer + patchInfo.patchClientExportsArrayAddr - patchInfoAddr, &clientExports[0], sizeof(clientExports[0]) * clientExports.size());
+    ::memcpy(buffer + patchInfo.patchLocationArrayAddr - patchInfoAddr, &patchLocations[0], sizeof(patchLocations[0]) * patchLocations.size());
 
     // GOT patch table
-    if ( !gotClients.empty() )
-        ::memcpy(buffer + patchInfo.gotClientsArrayAddr - patchInfoAddr, &gotClients[0], sizeof(gotClients[0]) * gotClients.size());
-    if ( !gotClientExports.empty() )
-        ::memcpy(buffer + patchInfo.gotClientExportsArrayAddr - patchInfoAddr, &gotClientExports[0], sizeof(gotClientExports[0]) * gotClientExports.size());
-    if ( !gotPatchLocations.empty() )
-        ::memcpy(buffer + patchInfo.gotLocationArrayAddr - patchInfoAddr, &gotPatchLocations[0], sizeof(gotPatchLocations[0]) * gotPatchLocations.size());
+    ::memcpy(buffer + patchInfo.gotClientsArrayAddr - patchInfoAddr, &gotClients[0], sizeof(gotClients[0]) * gotClients.size());
+    ::memcpy(buffer + patchInfo.gotClientExportsArrayAddr - patchInfoAddr, &gotClientExports[0], sizeof(gotClientExports[0]) * gotClientExports.size());
+    ::memcpy(buffer + patchInfo.gotLocationArrayAddr - patchInfoAddr, &gotPatchLocations[0], sizeof(gotPatchLocations[0]) * gotPatchLocations.size());
 
     // Shared export names
-    if ( !patchExportNames.empty() )
-        ::memcpy(buffer + patchInfo.patchExportNamesAddr - patchInfoAddr, &patchExportNames[0], patchExportNames.size());
+    ::memcpy(buffer + patchInfo.patchExportNamesAddr - patchInfoAddr, &patchExportNames[0], patchExportNames.size());
     
-    return Error();
-}
-
-Error PatchTableBuilder::prepare(const std::span<CacheDylib>& cacheDylibs,
-                                 const std::span<PatchInfo>& patchInfos)
-{
-    if ( cacheDylibs.size() != patchInfos.size() ) {
-        return Error("Mismatch in patch table inputs: %lld vs %lld",
-                     (uint64_t)cacheDylibs.size(), (uint64_t)patchInfos.size());
-    }
-
-    // Each dylib has a list of its uses of each bindTarget in its array.  We now need to combine those in
-    // to the list of uses of each exported symbol from each dylib
-    this->dylibClients.resize(cacheDylibs.size());
-    this->mergePatchInfos(cacheDylibs, patchInfos);
-
-    // We now have everything in the state we want, ie, each dylib has a list of who uses it.
-    // That is the form the patch table uses on-disk
-    this->calculateRequiredSpace(cacheDylibs);
-
     return Error();
 }
 
@@ -1491,25 +1207,29 @@
         return Error("Mismatch in patch table inputs: %lld vs %lld",
                      (uint64_t)cacheDylibs.size(), (uint64_t)patchInfos.size());
     }
-    if ( cacheDylibs.size() != dylibClients.size() ) {
-        return Error("Mismatch in patch table state: %lld vs %lld",
-                     (uint64_t)cacheDylibs.size(), (uint64_t)dylibClients.size());
-    }
-
+
+    // Each dylib has a list of its uses of each bindTarget in its array.  We now need to combine those in
+    // to the list of uses of each exported symbol from each dylib
+    this->dylibClients.resize(cacheDylibs.size());
+    this->mergePatchInfos(cacheDylibs, patchInfos);
+
+    // We now have everything in the state we want, ie, each dylib has a list of who uses it.
+    // That is the form the patch table uses on-disk
+    this->calculateRequiredSpace(cacheDylibs);
     this->calculatePatchTable(cacheDylibs, patchableObjCClasses, patchableCFObj2, cacheBaseAddress);
 
     return Error();
 }
 
 //
-// MARK: --- DyldCachePatchableLocation methods ---
+// MARK: --- dyld_cache_patchable_location methods ---
 //
 
-DyldCachePatchableLocation::DyldCachePatchableLocation(InputDylibVMOffset clientDylibOffset,
-                                                       dyld3::MachOFile::PointerMetaData pmd,
-                                                       uint64_t addend, bool isWeakImport)
-{
-    this->clientDylibOffset    = clientDylibOffset;
+dyld_cache_patchable_location::dyld_cache_patchable_location(CacheVMAddress cacheVMAddr,
+                                                             dyld3::MachOFile::PointerMetaData pmd,
+                                                             uint64_t addend, bool isWeakImport)
+{
+    this->cacheVMAddr          = cacheVMAddr;
     this->high7                = pmd.high8 >> 1;
     this->isWeakImport         = isWeakImport ? 1 : 0;
     this->unused               = 0;
@@ -1523,46 +1243,6 @@
     assert((this->high7 << 1) == pmd.high8);
 }
 
-//
-// MARK: --- DyldCachePatchableGOTLocation methods ---
-//
-
-DyldCachePatchableGOTLocation::DyldCachePatchableGOTLocation(const Chunk* clientGOT, VMOffset clientGOTOffset,
-                                                             dyld3::MachOFile::PointerMetaData pmd,
-                                                             uint64_t addend, bool isWeakImport)
-{
-    this->clientGOT            = clientGOT;
-    this->clientGOTOffset      = clientGOTOffset;
-    this->high7                = pmd.high8 >> 1;
-    this->isWeakImport         = isWeakImport ? 1 : 0;
-    this->unused               = 0;
-    this->authenticated        = pmd.authenticated;
-    this->usesAddressDiversity = pmd.usesAddrDiversity;
-    this->key                  = pmd.key;
-    this->discriminator        = pmd.diversity;
-    this->addend               = addend;
-    // check for truncations
-    assert(this->addend == addend);
-    assert((this->high7 << 1) == pmd.high8);
-}
-
-
-//
-// MARK: --- PatchTable methods ---
-//
-void DylibClients::calculateCacheUsedExports(const CacheDylib& cacheDylib)
-{
-    usedCacheDylibExports.reserve(usedInputDylibExports.size());
-    for ( const InputDylibVMAddress& inputVMAddr : this->usedInputDylibExports ) {
-        CacheVMAddress cacheVMAddr = cacheDylib.adjustor->adjustVMAddr(inputVMAddr);
-        usedCacheDylibExports.emplace_back(cacheVMAddr, inputVMAddr);
-    }
-
-    std::ranges::sort(usedCacheDylibExports, [](auto& l, auto& r) {
-        return l.cacheAddr < r.cacheAddr;
-    });
-}
-
 } // namespace cache_builder
 
 #endif // BUILDING_CACHE_BUILDER || BUILDING_CACHE_BUILDER_UNIT_TESTS