Loading...
cache_builder/CacheDylib.cpp dyld-1340 dyld-1285.19
--- dyld/dyld-1340/cache_builder/CacheDylib.cpp
+++ dyld/dyld-1285.19/cache_builder/CacheDylib.cpp
@@ -26,7 +26,6 @@
 #include "BuilderConfig.h"
 #include "BuilderOptions.h"
 #include "CacheDylib.h"
-#include "Chunk.h"
 #include "MachOFile.h"
 #include "MachOFileAbstraction.hpp"
 #include "Header.h"
@@ -93,8 +92,6 @@
     , inputLoadAddress(this->inputHdr->preferredLoadAddress())
     , installName(this->inputHdr->installName())
 {
-    if ( inputFile.mf )
-        inputImage = std::make_unique<mach_o::Image>(inputFile.mf, inputFile.size, Image::MappingKind::wholeSliceMapped);
 }
 
 CacheDylib::CacheDylib(std::string_view installName)
@@ -578,7 +575,7 @@
 
 void CacheDylib::copyRawSegments(const BuilderConfig& config, Timer::AggregateTimer& timer)
 {
-    const bool log = config.log.printDebugCacheLayout;
+    const bool log = config.log.printDebug;
 
     Timer::AggregateTimer::Scope timedScope(timer, "dylib copyRawSegments time");
 
@@ -970,6 +967,32 @@
             return;
         }
 
+        // Adjust the bind target.  We have a runtime offset for the target input dylib, but we need to know where that runtime Offset will
+        // map to in the target cache dylib
+        switch ( bindTarget.kind ) {
+            case BindTarget::Kind::absolute:
+                // Skip these.  They won't change due to shifting the input dylib in to the cache
+                break;
+            case BindTarget::Kind::inputImage: {
+                // Convert from an input dylib offset to the cache dylib offset
+                BindTarget::InputImage inputImage        = bindTarget.inputImage;
+                InputDylibVMAddress    targetInputVMAddr = inputImage.targetDylib->inputLoadAddress + inputImage.targetRuntimeOffset;
+                CacheVMAddress         targetCacheVMAddr = inputImage.targetDylib->adjustor->adjustVMAddr(targetInputVMAddr);
+
+                // Actually change the bindTarget to reflect the new type
+                bindTarget.kind = BindTarget::Kind::cacheImage;
+                bindTarget.inputImage.~InputImage();
+                bindTarget.cacheImage = (BindTarget::CacheImage) { VMOffset(targetCacheVMAddr - inputImage.targetDylib->cacheLoadAddress),
+                                                                   inputImage.targetDylib, inputImage.isWeakDef,
+                                                                   inputImage.isFunctionVariant, inputImage.functionVariantTableIndex };
+                break;
+            }
+            case BindTarget::Kind::cacheImage:
+                diag.error("Shouldn't see cacheImage fixups at this point");
+                stop = true;
+                return;
+        }
+
         bindTarget.addend = addend;
         bindTarget.isWeakImport = weakImport;
 #if DEBUG
@@ -995,20 +1018,50 @@
     }
     else if ( this->inputMF->hasOpcodeFixups() ) {
         // Use the fixups from the source dylib
-        this->inputMF->withFileLayout(diag, ^(const mach_o::Layout &layout) {
-            mach_o::Fixups fixups(layout);
-
-            bool allowLazyBinds = false;
-            fixups.forEachBindTarget(diag, allowLazyBinds, 0,
-                ^(const mach_o::Fixups::BindTargetInfo& info, bool& stop) {
-                    handleBindTarget(info.libOrdinal, info.symbolName, info.addend, info.weakImport, stop);
-                },
-                ^(const mach_o::Fixups::BindTargetInfo& info, bool& stop) {
-                    if ( !this->weakBindTargetsStartIndex.has_value() )
-                        this->weakBindTargetsStartIndex = this->bindTargets.size();
-                    handleBindTarget(info.libOrdinal, info.symbolName, info.addend, info.weakImport, stop);
-                });
+        mach_o::LinkeditLayout linkedit;
+        if ( !this->inputMF->getLinkeditLayout(diag, linkedit) ) {
+            diag.error("Couldn't get dylib layout");
+            return { };
+        }
+
+        // Use the segment layout from the cache dylib so that VMAddresses are correct
+        __block std::vector<mach_o::SegmentLayout> segmentLayout;
+        segmentLayout.reserve(this->segments.size());
+        for ( const DylibSegmentChunk& dylibSegment : this->segments ) {
+            mach_o::SegmentLayout segment;
+            segment.vmAddr      = dylibSegment.cacheVMAddress.rawValue();
+            segment.vmSize      = dylibSegment.cacheVMSize.rawValue();
+            segment.fileOffset  = dylibSegment.subCacheFileOffset.rawValue();
+            segment.fileSize    = dylibSegment.subCacheFileSize.rawValue();
+            segment.buffer      = dylibSegment.subCacheBuffer;
+
+            segment.kind        = mach_o::SegmentLayout::Kind::unknown;
+            if ( dylibSegment.segmentName == "__TEXT" ) {
+                segment.kind    = mach_o::SegmentLayout::Kind::text;
+            } else if ( dylibSegment.segmentName == "__LINKEDIT" ) {
+                segment.kind    = mach_o::SegmentLayout::Kind::linkedit;
+            }
+            segmentLayout.push_back(segment);
+        }
+
+        // The cache segments don't have the permissions.  Get that from the load commands
+        this->cacheHdr->forEachSegment(^(const Header::SegmentInfo& info, bool& stop) {
+            segmentLayout[info.segmentIndex].protections = info.initProt;
         });
+
+        mach_o::Layout layout(this->inputMF, { segmentLayout.data(), segmentLayout.data() + segmentLayout.size() }, linkedit);
+        mach_o::Fixups fixups(layout);
+
+        bool allowLazyBinds = false;
+        fixups.forEachBindTarget(diag, allowLazyBinds, 0,
+            ^(const mach_o::Fixups::BindTargetInfo& info, bool& stop) {
+                handleBindTarget(info.libOrdinal, info.symbolName, info.addend, info.weakImport, stop);
+            },
+            ^(const mach_o::Fixups::BindTargetInfo& info, bool& stop) {
+                if ( !this->weakBindTargetsStartIndex.has_value() )
+                    this->weakBindTargetsStartIndex = this->bindTargets.size();
+                handleBindTarget(info.libOrdinal, info.symbolName, info.addend, info.weakImport, stop);
+            });
     }
     else {
         // Cache dylibs shouldn't use old style fixups.
@@ -1025,13 +1078,34 @@
                               uint32_t bindOrdinal, uint32_t segIndex,
                               dyld3::MachOFile::ChainedFixupPointerOnDisk* fixupLoc,
                               CacheVMAddress fixupVMAddr, MachOFile::PointerMetaData pmd,
-                              CoalescedGOTsMap& coalescedGOTs, CoalescedGOTsMap& coalescedAuthGOTs,
-                              CoalescedGOTsMap& coalescedAuthPtrs, PatchInfo& dylibPatchInfo,
+                              CoalescedGOTMap& coalescedGOTs, CoalescedGOTMap& coalescedAuthGOTs,
+                              CoalescedGOTMap& coalescedAuthPtrs, PatchInfo& dylibPatchInfo,
                               FunctionVariantsOptimizer& functionVariantsOptimizer)
 {
     switch ( bindTarget.kind ) {
         case BindTarget::Kind::absolute: {
             uint64_t targetValue = bindTarget.absolute.value + addend;
+
+            if ( needsPatchTable ) {
+                auto checkGOTs = ^(CoalescedGOTMap& gotMap, std::vector<std::vector<PatchInfo::GOTInfo>>& gotInfo) {
+                    auto gotIt = gotMap.find(fixupVMAddr);
+                    if ( gotIt != gotMap.end() ) {
+                        // Probably a missing weak import.  Rewrite the original GOT anyway, but also the coalesced one
+                        dyld_cache_patchable_location patchLoc(gotIt->second, pmd, addend, bindTarget.isWeakImport);
+                        auto& gotUses = gotInfo[bindOrdinal];
+                        gotUses.emplace_back((PatchInfo::GOTInfo){ patchLoc, VMOffset(targetValue) });
+                        return true;
+                    }
+                    return false;
+                };
+                if ( checkGOTs(coalescedGOTs, dylibPatchInfo.bindGOTUses) ) {
+                    // normal GOT
+                } else if ( checkGOTs(coalescedAuthGOTs, dylibPatchInfo.bindAuthGOTUses) ) {
+                    // auth GOT
+                } else if ( checkGOTs(coalescedAuthPtrs, dylibPatchInfo.bindAuthPtrUses) ) {
+                    // auth ptr
+                }
+            }
 
             if ( config.layout.is64 ) {
                 fixupLoc->raw64 = targetValue;
@@ -1079,12 +1153,26 @@
             // note down the fixup to the GOT.  We can't just apply the GOT fixup, as we might be running in parallel with
             // other threads all trying to do the same thing
             if( needsPatchTable ) {
-                // The GOT map is keyed by the input VMAddr, so convert back to that
-                VMOffset segmentVMOffset = fixupVMAddr - this->segments[segIndex].cacheVMAddress;
-                InputDylibVMAddress inputFixupVMAddr = this->segments[segIndex].inputVMAddress + segmentVMOffset;
-                auto checkGOTs = ^(CoalescedGOTsMap& gotMap) {
-                    auto gotIt = gotMap.find(inputFixupVMAddr);
+                uint64_t                   patchTableAddend = addend;
+                MachOFile::PointerMetaData patchTablePMD    = pmd;
+                uint64_t                   addendHigh8      = addend >> 56;
+                if ( addendHigh8 != 0 ) {
+                    // Put the high8 from the addend in to the high8 of the patch
+                    assert(patchTablePMD.high8 == 0);
+                    patchTablePMD.high8 = (uint32_t)addendHigh8;
+
+                    // Remove high8 from the addend
+                    patchTableAddend = patchTableAddend & 0x00FFFFFFFFFFFFFFULL;
+                }
+
+                VMOffset finalVMOffset = CacheVMAddress(finalVMAddrWithAddend) - config.layout.cacheBaseAddress;
+
+                auto checkGOTs = ^(CoalescedGOTMap& gotMap, std::vector<std::vector<PatchInfo::GOTInfo>>& gotInfo) {
+                    auto gotIt = gotMap.find(fixupVMAddr);
                     if ( gotIt != gotMap.end() ) {
+                        dyld_cache_patchable_location patchLoc(gotIt->second, patchTablePMD, patchTableAddend, bindTarget.isWeakImport);
+                        auto& gotUses = gotInfo[bindOrdinal];
+                        gotUses.emplace_back((PatchInfo::GOTInfo){ patchLoc, finalVMOffset });
 
                         // NULL out this entry
                         if ( config.layout.is64 ) {
@@ -1099,9 +1187,15 @@
                     }
                     return false;
                 };
-                if ( checkGOTs(coalescedGOTs) || checkGOTs(coalescedAuthGOTs) || checkGOTs(coalescedAuthPtrs) ) {
-                    // normal GOT/auth GOT/auth ptr
+                if ( checkGOTs(coalescedGOTs, dylibPatchInfo.bindGOTUses) ) {
+                    // normal GOT
+                } else if ( checkGOTs(coalescedAuthGOTs, dylibPatchInfo.bindAuthGOTUses) ) {
+                    // auth GOT
+                } else if ( checkGOTs(coalescedAuthPtrs, dylibPatchInfo.bindAuthPtrUses) ) {
+                    // auth ptr
                 } else {
+                    // Location wasn't coalesced.  So add to the regular list of uses
+                    dylibPatchInfo.bindUses[bindOrdinal].emplace_back(fixupVMAddr, patchTablePMD, patchTableAddend, bindTarget.isWeakImport);
                     // if target is a function variant, record that dyld may need to update pointer at launch
                     if ( bindTarget.cacheImage.isFunctionVariant ) {
                         uint64_t fvTableVmAddr = 0;
@@ -1135,8 +1229,8 @@
 }
 
 void CacheDylib::bindWithChainedFixups(Diagnostics& diag, const BuilderConfig& config,
-                                       CoalescedGOTsMap& coalescedGOTs, CoalescedGOTsMap& coalescedAuthGOTs,
-                                       CoalescedGOTsMap& coalescedAuthPtrs, PatchInfo& dylibPatchInfo,
+                                       CoalescedGOTMap& coalescedGOTs, CoalescedGOTMap& coalescedAuthGOTs,
+                                       CoalescedGOTMap& coalescedAuthPtrs, PatchInfo& dylibPatchInfo,
                                        FunctionVariantsOptimizer& functionVariantsOptimizer)
 {
     auto fixupHandler = ^(MachOFile::ChainedFixupPointerOnDisk* fixupLoc, uint16_t chainedFormat,
@@ -1201,8 +1295,7 @@
         this->bindLocation(diag, config, targetInTable, addend, bindOrdinal, segIndex,
                            fixupLoc, fixupVMAddr, pmd,
                            coalescedGOTs, coalescedAuthGOTs, 
-                           coalescedAuthPtrs, dylibPatchInfo,
-                           functionVariantsOptimizer);
+                           coalescedAuthPtrs, dylibPatchInfo, functionVariantsOptimizer);
     };
 
     this->inputMF->withFileLayout(diag, ^(const mach_o::Layout &layout) {
@@ -1234,8 +1327,8 @@
 }
 
 void CacheDylib::bindWithOpcodeFixups(Diagnostics& diag, const BuilderConfig& config,
-                                      CoalescedGOTsMap& coalescedGOTs, CoalescedGOTsMap& coalescedAuthGOTs,
-                                      CoalescedGOTsMap& coalescedAuthPtrs, PatchInfo& dylibPatchInfo,
+                                      CoalescedGOTMap& coalescedGOTs, CoalescedGOTMap& coalescedAuthGOTs,
+                                      CoalescedGOTMap& coalescedAuthPtrs, PatchInfo& dylibPatchInfo,
                                       FunctionVariantsOptimizer& functionVariantsOptimizer)
 {
     auto handleFixup = ^(uint64_t fixupRuntimeOffset, int bindOrdinal, uint32_t segmentIndex, bool& stopSegment) {
@@ -1257,8 +1350,7 @@
         this->bindLocation(diag, config, targetInTable, addend, bindOrdinal, segmentIndex,
                            (dyld3::MachOFile::ChainedFixupPointerOnDisk*)fixupLoc,
                            fixupVMAddr, dyld3::MachOFile::PointerMetaData(),
-                           coalescedGOTs, coalescedAuthGOTs, coalescedAuthPtrs, dylibPatchInfo,
-                           functionVariantsOptimizer);
+                           coalescedGOTs, coalescedAuthGOTs, coalescedAuthPtrs, dylibPatchInfo, functionVariantsOptimizer);
     };
 
     // Use the fixups from the source dylib
@@ -1342,102 +1434,37 @@
         });
 }
 
-void CacheDylib::calculateBindLocationPatchInfo(Diagnostics& diag, const BuilderConfig& config,
-                                                const BindTarget& bindTarget, uint64_t addend,
-                                                uint32_t bindOrdinal, uint32_t segIndex,
-                                                InputDylibVMAddress fixupVMAddr, MachOFile::PointerMetaData pmd,
-                                                CoalescedGOTsMap& coalescedGOTs, CoalescedGOTsMap& coalescedAuthGOTs,
-                                                CoalescedGOTsMap& coalescedAuthPtrs, PatchInfo& dylibPatchInfo)
-{
-    switch ( bindTarget.kind ) {
-        case BindTarget::Kind::absolute: {
-            uint64_t targetValue = bindTarget.absolute.value + addend;
-
-            auto checkGOTs = ^(CoalescedGOTsMap& gotMap, std::vector<std::vector<PatchInfo::GOTInfo>>& gotInfo) {
-                auto gotIt = gotMap.find(fixupVMAddr);
-                if ( gotIt != gotMap.end() ) {
-                    // Probably a missing weak import.  Rewrite the original GOT anyway, but also the coalesced one
-                    const ChunkPlusOffset gotPlusOffset = gotIt->second;
-                    DyldCachePatchableGOTLocation patchLoc(gotPlusOffset.first, gotPlusOffset.second, pmd, addend, bindTarget.isWeakImport);
-                    auto& gotUses = gotInfo[bindOrdinal];
-                    gotUses.emplace_back((PatchInfo::GOTInfo){ patchLoc, targetValue });
-                    return true;
-                }
-                return false;
-            };
-            if ( checkGOTs(coalescedGOTs, dylibPatchInfo.bindGOTUses) ) {
-                // normal GOT
-            } else if ( checkGOTs(coalescedAuthGOTs, dylibPatchInfo.bindAuthGOTUses) ) {
-                // auth GOT
-            } else if ( checkGOTs(coalescedAuthPtrs, dylibPatchInfo.bindAuthPtrUses) ) {
-                // auth ptr
-            }
-            return;
-        }
-        case BindTarget::Kind::inputImage: {
-            InputDylibVMAddress targetDylibLoadAddress = bindTarget.inputImage.targetDylib->inputLoadAddress;
-            InputDylibVMAddress targetVMAddr = targetDylibLoadAddress + bindTarget.inputImage.targetRuntimeOffset;
-            uint64_t            finalTargetVMAddrWithAddend  = targetVMAddr.rawValue() + addend;
-
-            // Work out if the location we just wrote is a coalesced GOT.  If so, NULL the current location and
-            // note down the fixup to the GOT.  We can't just apply the GOT fixup, as we might be running in parallel with
-            // other threads all trying to do the same thing
-            uint64_t                   patchTableAddend = addend;
-            MachOFile::PointerMetaData patchTablePMD    = pmd;
-            uint64_t                   addendHigh8      = addend >> 56;
-            if ( addendHigh8 != 0 ) {
-                // Put the high8 from the addend in to the high8 of the patch
-                assert(patchTablePMD.high8 == 0);
-                patchTablePMD.high8 = (uint32_t)addendHigh8;
-
-                // Remove high8 from the addend
-                patchTableAddend = patchTableAddend & 0x00FFFFFFFFFFFFFFULL;
-            }
-
-            InputDylibVMOffset finalTargetVMOffset = InputDylibVMAddress(finalTargetVMAddrWithAddend) - targetDylibLoadAddress;
-
-            auto checkGOTs = ^(CoalescedGOTsMap& gotMap, std::vector<std::vector<PatchInfo::GOTInfo>>& gotInfo) {
-                auto gotIt = gotMap.find(fixupVMAddr);
-                if ( gotIt != gotMap.end() ) {
-                    const ChunkPlusOffset gotPlusOffset = gotIt->second;
-                    DyldCachePatchableGOTLocation patchLoc(gotPlusOffset.first, gotPlusOffset.second, patchTablePMD, patchTableAddend, bindTarget.isWeakImport);
-                    auto& gotUses = gotInfo[bindOrdinal];
-                    DylibOffset dylibOffset = { bindTarget.inputImage.targetDylib, finalTargetVMOffset };
-                    gotUses.emplace_back((PatchInfo::GOTInfo){ patchLoc, dylibOffset });
-                    return true;
-                }
-                return false;
-            };
-            if ( checkGOTs(coalescedGOTs, dylibPatchInfo.bindGOTUses) ) {
-                // normal GOT
-            } else if ( checkGOTs(coalescedAuthGOTs, dylibPatchInfo.bindAuthGOTUses) ) {
-                // auth GOT
-            } else if ( checkGOTs(coalescedAuthPtrs, dylibPatchInfo.bindAuthPtrUses) ) {
-                // auth ptr
-            } else {
-                // Location wasn't coalesced.  So add to the regular list of uses
-                InputDylibVMOffset fixupVMOffset = fixupVMAddr - this->inputLoadAddress;
-                DyldCachePatchableLocation patchLoc = { fixupVMOffset, patchTablePMD, patchTableAddend, bindTarget.isWeakImport };
-                dylibPatchInfo.bindUses[bindOrdinal].push_back(patchLoc);
-            }
-            break;
-        }
-        case BindTarget::Kind::cacheImage: {
-            diag.error("Input binds should not have been converted to cache binds in %s: %d",
-                       this->installName.data(), bindOrdinal);
-            return;
-        }
-    }
-}
-
-void CacheDylib::calcuatePatchInfo(Diagnostics& diag, const BuilderConfig& config, Timer::AggregateTimer& timer,
-                                   PatchInfo& dylibPatchInfo)
-{
-    Timer::AggregateTimer::Scope timedScope(timer, "dylib patch info calculation time");
-
-    __block CoalescedGOTsMap coalescedGOTs      = optimizedSections.gots.getCoalescedGOTsMap();
-    __block CoalescedGOTsMap coalescedAuthGOTs  = optimizedSections.auth_gots.getCoalescedGOTsMap();
-    __block CoalescedGOTsMap coalescedAuthPtrs  = optimizedSections.auth_ptrs.getCoalescedGOTsMap();
+void CacheDylib::bind(Diagnostics& diag, const BuilderConfig& config, Timer::AggregateTimer& timer,
+                      PatchInfo& dylibPatchInfo, FunctionVariantsOptimizer& functionVariantsOptimizer)
+{
+    Timer::AggregateTimer::Scope timedScope(timer, "dylib bind time");
+
+    // As we are running in parallel, addresses in other dylibs may not have been shifted yet.  We may also
+    // race looking at the export trie in a target dylib, while it is being shifted by AdjustDylibSegments.
+    // Given that, we'll look at our own cache dylib, but everyone elses input dylib, as those won't mutate
+
+    // Map from where the GOT is located in the dylib to where its located in the coalesced section
+    typedef std::unordered_map<const CacheVMAddress, CacheVMAddress, CacheVMAddressHash, CacheVMAddressEqual> CoalescedGOTsMap;
+    auto mapGOTs = [](const DylibSectionCoalescer::OptimizedSection& gotSection, std::span<DylibSegmentChunk> dylibSegments,
+                      CoalescedGOTsMap& coalescedGOTs) {
+        if ( !gotSection.offsetMap.empty() ) {
+            uint32_t segmentIndex = gotSection.segmentIndex.value();
+            CacheVMAddress dylibGOTBaseVMAddr = dylibSegments[segmentIndex].cacheVMAddress + gotSection.sectionVMOffsetInSegment;
+            CacheVMAddress cacheGOTBaseVMAddr = gotSection.subCacheSection->cacheChunk->cacheVMAddress;
+            for ( const auto& dylibOffsetAndCacheOffset : gotSection.offsetMap ) {
+                VMOffset dylibSectionOffset((uint64_t)dylibOffsetAndCacheOffset.first);
+                VMOffset cacheSectionOffset((uint64_t)dylibOffsetAndCacheOffset.second);
+                coalescedGOTs[dylibGOTBaseVMAddr + dylibSectionOffset] = cacheGOTBaseVMAddr + cacheSectionOffset;
+            }
+        }
+    };
+    CoalescedGOTsMap coalescedGOTs;
+    CoalescedGOTsMap coalescedAuthGOTs;
+    CoalescedGOTsMap coalescedAuthPtrs;
+
+    mapGOTs(optimizedSections.gots, this->segments, coalescedGOTs);
+    mapGOTs(optimizedSections.auth_gots, this->segments, coalescedAuthGOTs);
+    mapGOTs(optimizedSections.auth_ptrs, this->segments, coalescedAuthPtrs);
 
     // Track which locations this dylib uses in other dylibs.  One per bindTarget
     dylibPatchInfo.bindUses.resize(this->bindTargets.size());
@@ -1445,78 +1472,10 @@
     dylibPatchInfo.bindAuthGOTUses.resize(this->bindTargets.size());
     dylibPatchInfo.bindAuthPtrUses.resize(this->bindTargets.size());
 
-    if ( !needsPatchTable )
-        return;
-
-    auto handleFixup = ^(InputDylibVMAddress fixupVMAddr, int64_t embeddedAddend, int bindOrdinal, dyld3::MachOFile::PointerMetaData pmd,
-                         uint32_t segmentIndex, bool& stopSegment) {
-        if ( bindOrdinal >= this->bindTargets.size() ) {
-            diag.error("out of range bind ordinal %d (max %lu)", bindOrdinal, this->bindTargets.size());
-            stopSegment   = true;
-            return;
-        }
-
-        const BindTarget& targetInTable = this->bindTargets[bindOrdinal];
-        uint64_t          addend        = targetInTable.addend + embeddedAddend;
-
-        this->calculateBindLocationPatchInfo(diag, config, targetInTable, addend, bindOrdinal, segmentIndex,
-                                             fixupVMAddr, pmd,
-                                             coalescedGOTs, coalescedAuthGOTs, coalescedAuthPtrs, dylibPatchInfo);
-    };
-
-    this->inputMF->withFileLayout(diag, ^(const mach_o::Layout& layout) {
-        mach_o::Fixups fixups(layout);
-        if ( this->inputMF->hasChainedFixups() ) {
-            fixups.withChainStarts(diag, ^(const dyld_chained_starts_in_image* starts) {
-                fixups.forEachFixupChainSegment(diag, starts, ^(const dyld_chained_starts_in_segment *segInfo, uint32_t segIndex, bool &stopSegment) {
-                    InputDylibVMAddress segmentVMAddr = this->segments[segIndex].inputVMAddress;
-                    fixups.forEachFixupInSegmentChains(diag, segInfo, segIndex, true,
-                                                       ^(dyld3::MachOFile::ChainedFixupPointerOnDisk *fixupLocation, uint64_t fixupSegmentOffset, bool &stopChain) {
-                        uint32_t bindOrdinal = 0;
-                        int64_t embeddedAddend = 0;
-                        if ( fixupLocation->isBind(segInfo->pointer_format, bindOrdinal, embeddedAddend) ) {
-                            MachOFile::PointerMetaData pmd(fixupLocation, segInfo->pointer_format);
-                            handleFixup(segmentVMAddr + VMOffset(fixupSegmentOffset), embeddedAddend, bindOrdinal, pmd, segIndex, stopChain);
-                        }
-                    });
-                });
-            });
-        } else if ( this->inputMF->hasOpcodeFixups() ) {
-            const dyld3::MachOFile::PointerMetaData pmd;
-            fixups.forEachBindLocation_Opcodes(diag,
-                                               ^(uint64_t runtimeOffset, uint32_t segmentIndex, unsigned int targetIndex, bool& stop) {
-                InputDylibVMAddress fixupVMAddr = this->inputLoadAddress + VMOffset(runtimeOffset);
-                handleFixup(fixupVMAddr, 0, targetIndex, pmd, segmentIndex, stop);
-            },
-            ^(uint64_t runtimeOffset, uint32_t segmentIndex, unsigned int overrideBindTargetIndex, bool& stop) {
-                assert(this->weakBindTargetsStartIndex.has_value());
-                InputDylibVMAddress fixupVMAddr = this->inputLoadAddress + VMOffset(runtimeOffset);
-                handleFixup(fixupVMAddr, 0, this->weakBindTargetsStartIndex.value() + overrideBindTargetIndex, pmd, segmentIndex, stop);
-            });
-        } else {
-            // Cache dylibs shouldn't use old style fixups.
-        }
-    });
-}
-
-void CacheDylib::bind(Diagnostics& diag, const BuilderConfig& config, Timer::AggregateTimer& timer,
-                      PatchInfo& dylibPatchInfo, FunctionVariantsOptimizer& functionVariantsOptimizer)
-{
-    Timer::AggregateTimer::Scope timedScope(timer, "dylib bind time");
-
-    // As we are running in parallel, addresses in other dylibs may not have been shifted yet.  We may also
-    // race looking at the export trie in a target dylib, while it is being shifted by AdjustDylibSegments.
-    // Given that, we'll look at our own cache dylib, but everyone elses input dylib, as those won't mutate
-    CoalescedGOTsMap coalescedGOTs      = optimizedSections.gots.getCoalescedGOTsMap();
-    CoalescedGOTsMap coalescedAuthGOTs  = optimizedSections.auth_gots.getCoalescedGOTsMap();
-    CoalescedGOTsMap coalescedAuthPtrs  = optimizedSections.auth_ptrs.getCoalescedGOTsMap();
-
     if ( this->inputMF->hasChainedFixups() )
-        bindWithChainedFixups(diag, config, coalescedGOTs, coalescedAuthGOTs, coalescedAuthPtrs, dylibPatchInfo,
-                              functionVariantsOptimizer);
+        bindWithChainedFixups(diag, config, coalescedGOTs, coalescedAuthGOTs, coalescedAuthPtrs, dylibPatchInfo, functionVariantsOptimizer);
     else if ( this->inputMF->hasOpcodeFixups() ) {
-        bindWithOpcodeFixups(diag, config, coalescedGOTs, coalescedAuthGOTs, coalescedAuthPtrs, dylibPatchInfo,
-                             functionVariantsOptimizer);
+        bindWithOpcodeFixups(diag, config, coalescedGOTs, coalescedAuthGOTs, coalescedAuthPtrs, dylibPatchInfo, functionVariantsOptimizer);
     } else {
         // Cache dylibs shouldn't use old style fixups.
     }
@@ -1875,7 +1834,7 @@
                                             Timer::AggregateTimer& timer,
                                             const ObjCStringsChunk* selectorStringsChunk)
 {
-    const bool logSelectors = config.log.printDebugCacheLayout;
+    const bool logSelectors = config.log.printDebug;
 
     Timer::AggregateTimer::Scope timedScope(timer, "dylib optimizeLoadsFromConstants time");
 
@@ -2059,9 +2018,9 @@
     if ( bindTarget.kind != BindTarget::Kind::inputImage )
         return Error("Couldn't build IMP caches because: symbol is wrong kind");
 
-    BindTarget::InputImage bindInputImage    = bindTarget.inputImage;
-    InputDylibVMAddress    targetInputVMAddr = bindInputImage.targetDylib->inputLoadAddress + bindInputImage.targetRuntimeOffset;
-    CacheVMAddress         targetCacheVMAddr = bindInputImage.targetDylib->adjustor->adjustVMAddr(targetInputVMAddr);
+    BindTarget::InputImage inputImage        = bindTarget.inputImage;
+    InputDylibVMAddress    targetInputVMAddr = inputImage.targetDylib->inputLoadAddress + inputImage.targetRuntimeOffset;
+    CacheVMAddress         targetCacheVMAddr = inputImage.targetDylib->adjustor->adjustVMAddr(targetInputVMAddr);
 
     // Find the segment for the content
     for ( DylibSegmentChunk& segment : this->segments ) {
@@ -2126,7 +2085,7 @@
     if ( !objcIMPCachesOptimizer.builder )
         return Error();
 
-    const bool log = config.log.printDebugIMPCaches;
+    const bool log = config.log.printDebug;
 
     Timer::AggregateTimer::Scope timedScope(timer, "emitObjCIMPCaches time");
 
@@ -2225,13 +2184,13 @@
                 currentBucket->impOffset = 0;
             } else {
                 imp_caches::BucketMethod bucketMethod = {
+                    .installName = bucket.installName,
                     .className = bucket.className,
                     .methodName = bucket.methodName,
                     .isInstanceMethod = bucket.isInstanceMethod
                 };
-                const auto& dylibMethodMap = objcIMPCachesOptimizer.methodMap.at(bucket.installName);
-                auto bucketIt = dylibMethodMap.find(bucketMethod);
-                assert(bucketIt != dylibMethodMap.end());
+                auto bucketIt = objcIMPCachesOptimizer.methodMap.find(bucketMethod);
+                assert(bucketIt != objcIMPCachesOptimizer.methodMap.end());
 
                 const ObjCIMPCachesOptimizer::InputDylibLocation& bucketInputLocation = bucketIt->second;
                 CacheVMAddress methodVMAddr = bucketInputLocation.first->adjustor->adjustVMAddr(bucketInputLocation.second);
@@ -2304,9 +2263,9 @@
             const BindTarget::CacheImage& cacheImageTarget = bindTarget.cacheImage;
             CacheVMAddress bindTargetVMAddr = cacheImageTarget.targetDylib->cacheLoadAddress + cacheImageTarget.targetRuntimeOffset;
 
+
             for ( const PatchInfo::GOTInfo& gotInfo : clientUses ) {
-                CacheVMAddress gotVMAddr = gotInfo.useLocation.clientGOT->cacheVMAddress + gotInfo.useLocation.clientGOTOffset;
-                gotToTargetMap[gotVMAddr] = bindTargetVMAddr;
+                gotToTargetMap[gotInfo.patchInfo.cacheVMAddr] = bindTargetVMAddr;
             }
         }
     }
@@ -2428,7 +2387,7 @@
                     // Customer stub
                     uint8_t* newStubBuffer = customerStubs.subCacheBuffer + stubOffset;
                     StubOptimizer::generateArm64StubTo(newStubBuffer, newStubVMAddr.rawValue(),
-                                                       targetLPAddr, gotTargetVMAddr->rawValue());
+                                                       gotTargetVMAddr->rawValue());
                 }
             } else if ( this->cacheHdr->isArch("arm64e") ) {
                 uint64_t targetLPAddr = StubOptimizer::gotAddrFromArm64eStub(diag, this->installName,
@@ -2456,7 +2415,7 @@
                     // Customer stub
                     uint8_t* newStubBuffer = customerStubs.subCacheBuffer + stubOffset;
                     StubOptimizer::generateArm64eStubTo(newStubBuffer, newStubVMAddr.rawValue(),
-                                                        targetLPAddr, gotTargetVMAddr->rawValue());
+                                                        gotTargetVMAddr->rawValue());
                 }
             } else if ( this->cacheHdr->isArch("arm64_32") ) {
                 uint64_t targetLPAddr = StubOptimizer::gotAddrFromArm64_32Stub(diag, this->installName,
@@ -2999,7 +2958,7 @@
 metadata_visitor::Visitor CacheDylib::makeCacheVisitor(const BuilderConfig& config) const
 {
     // Get the segment ranges.  We need this as the dylib's segments are in different buffers, not in VM layout
-    __block std::vector<metadata_visitor::Segment> cacheSegments;
+    std::vector<metadata_visitor::Segment> cacheSegments;
     cacheSegments.reserve(this->segments.size());
     for ( uint32_t segIndex = 0; segIndex != this->segments.size(); ++segIndex ) {
         const DylibSegmentChunk& segmentInfo = this->segments[segIndex];
@@ -3018,18 +2977,27 @@
         cacheSegments.push_back(std::move(segment));
     }
 
+    auto addGots = [&cacheSegments](const DylibSectionCoalescer::OptimizedSection& gotSection) {
+        if ( gotSection.subCacheSection != nullptr ) {
+            auto* chunk = gotSection.subCacheSection->cacheChunk;
+            if ( chunk != nullptr ) {
+                metadata_visitor::Segment segment;
+                segment.startVMAddr = VMAddress(chunk->cacheVMAddress.rawValue());
+                segment.endVMAddr   = VMAddress((chunk->cacheVMAddress + chunk->cacheVMSize).rawValue());
+                segment.bufferStart = chunk->subCacheBuffer;
+
+                // Cache segments never have a chained format. They always use the Fixup struct
+                segment.onDiskDylibChainedPointerFormat = { };
+
+                cacheSegments.push_back(std::move(segment));
+            }
+        }
+    };
+
     // Add the GOTs too, if we have them
-    optimizedSections.forEachCacheGOTChunk(^(const cache_builder::Chunk* chunk) {
-        metadata_visitor::Segment segment;
-        segment.startVMAddr = VMAddress(chunk->cacheVMAddress.rawValue());
-        segment.endVMAddr   = VMAddress((chunk->cacheVMAddress + chunk->cacheVMSize).rawValue());
-        segment.bufferStart = chunk->subCacheBuffer;
-
-        // Cache segments never have a chained format. They always use the Fixup struct
-        segment.onDiskDylibChainedPointerFormat = { };
-
-        cacheSegments.push_back(std::move(segment));
-    });
+    addGots(optimizedSections.gots);
+    addGots(optimizedSections.auth_gots);
+    addGots(optimizedSections.auth_ptrs);
 
     std::vector<uint64_t> unusedBindTargets;
     metadata_visitor::Visitor visitor(config.layout.cacheBaseAddress, this->cacheMF,