Loading...
cache-builder/OptimizerObjC.cpp dyld-1042.1 dyld-960
--- dyld/dyld-1042.1/cache-builder/OptimizerObjC.cpp
+++ dyld/dyld-960/cache-builder/OptimizerObjC.cpp
@@ -173,7 +173,7 @@
 
     pint_t getVMAddress(pint_t index) const {
         if ( index >= _count ) {
-            _cache->diagnostics().error("index out of range in section %s", _section->sectname());
+            _cache->diagnostics().error("index out of range in section (%s)", _section->sectname());
             return 0;
         }
         return (pint_t)P::getP(_base[index]);
@@ -189,7 +189,7 @@
 
     void setVMAddress(pint_t index, pint_t value) {
         if ( index >= _count ) {
-            _cache->diagnostics().error("index out of range in section %s", _section->sectname());
+            _cache->diagnostics().error("index out of range in section (%s)", _section->sectname());
             return;
         }
         P::setP(_base[index], value);
@@ -234,7 +234,7 @@
 
     T& get(uint64_t index) const { 
         if (index >= _count) {
-            _cache->diagnostics().error("index out of range in section %s", _section->sectname());
+            _cache->diagnostics().error("index out of range in section (%s)", _section->sectname());
         }
         return _base[index];
     }
@@ -273,9 +273,10 @@
         return (pint_t)element->second;
     }
 
-    void visitCoalescedStrings(const CacheBuilder::CoalescedStringsSection& coalescedMethodNames) {
-        for (const auto& stringAndOffset : coalescedMethodNames.stringsToOffsets) {
-            uint64_t vmAddr = coalescedMethodNames.bufferVMAddr + stringAndOffset.second;
+    void visitCoalescedStrings(const CacheBuilder::CacheCoalescedText& coalescedText) {
+        const CacheBuilder::CacheCoalescedText::StringSection& methodNames = coalescedText.getSectionData("__objc_methname");
+        for (const auto& stringAndOffset : methodNames.stringsToOffsets) {
+            uint64_t vmAddr = methodNames.bufferVMAddr + stringAndOffset.second;
             _selectorStrings[stringAndOffset.first.data()] = vmAddr;
         }
     }
@@ -307,9 +308,10 @@
 
     ClassListBuilder(const std::unordered_map<const macho_header<P>*, uint16_t>& dylibIndices) : _dylibIndices(dylibIndices) { }
 
-    void visitCoalescedStrings(const CacheBuilder::CoalescedStringsSection& coalescedClassNames) {
-        for (const auto& stringAndOffset : coalescedClassNames.stringsToOffsets) {
-            uint64_t vmAddr = coalescedClassNames.bufferVMAddr + stringAndOffset.second;
+    void visitCoalescedStrings(const CacheBuilder::CacheCoalescedText& coalescedText) {
+        const CacheBuilder::CacheCoalescedText::StringSection& classNames = coalescedText.getSectionData("__objc_classname");
+        for (const auto& stringAndOffset : classNames.stringsToOffsets) {
+            uint64_t vmAddr = classNames.bufferVMAddr + stringAndOffset.second;
             _uniquedClassNames[stringAndOffset.first.data()] = vmAddr;
         }
     }
@@ -464,7 +466,7 @@
     T inlinedSelectorsVMAddrEnd;
 };
 
-template <typename P, typename H>
+template <typename P>
 class IMPCachesEmitter
 {
     typedef typename P::uint_t pint_t;
@@ -487,11 +489,30 @@
     std::map<std::string_view, const macho_header<P>*> _dylibs;
     const std::vector<const IMPCaches::Selector*> inlinedSelectors;
 
+    struct ImpCacheHeader {
+        int32_t  fallback_class_offset;
+        uint32_t cache_shift :  5;
+        uint32_t cache_mask  : 11;
+        uint32_t occupied    : 14;
+        uint32_t has_inlines :  1;
+        uint32_t bit_one     :  1;
+    };
+
+    struct ImpCacheEntry_v1 {
+        uint32_t selOffset;
+        uint32_t impOffset;
+    };
+
+    struct ImpCacheEntry_v2 {
+        int64_t impOffset : 38;
+        uint64_t selOffset : 26;
+    };
+
 public:
 
     static size_t sizeForImpCacheWithCount(int entries) {
         static_assert(sizeof(ImpCacheEntry_v1) == sizeof(ImpCacheEntry_v2));
-        return sizeof(H) + entries * sizeof(ImpCacheEntry_v1);
+        return sizeof(ImpCacheHeader) + entries * sizeof(ImpCacheEntry_v1);
     }
 
     struct ImpCacheContents {
@@ -629,7 +650,6 @@
             cachesFormatVersion(cachesVersion), objcASLRTracker(objcASLRTracker) {
             for (const SharedCacheBuilder::DylibInfo& d : dylibInfos) {
                 _dylibInfos[d.dylibID] = &d;
-                _dylibInfos[d.input->mappedFile.mh->installName()] = &d;
             }
             for (const macho_header<P>* d : dylibs) {
                 const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*) d;
@@ -664,7 +684,7 @@
         const char* className = cls->getName(cache);
 
         if (cls->getVTable(cache) != 0) {
-            diag.error("Class '%s' has non-zero vtable\n", className);
+            diag.error("Class (%s) has non-zero vtable\n", className);
             return;
         }
 
@@ -697,12 +717,20 @@
             printf("Writing cache for %sclass %s (%#08llx)\n", cls->isMetaClass(cache) ? "meta" : "", className, clsVMAddr);
         }
 
+        struct ImpCacheHeader {
+            int32_t  fallback_class_offset;
+            uint32_t cache_shift :  5;
+            uint32_t cache_mask  : 11;
+            uint32_t occupied    : 14;
+            uint32_t has_inlines :  1;
+            uint32_t bit_one     :  1;
+        };
         pint_t* vtableAddr = cls->getVTableAddress();
 
         // the alignment of ImpCaches to 16 bytes is only needed for arm64_32.
-        H* cachePtr = (H*)align_buffer(readOnlyBuffer, sizeof(pint_t) == 4 ? 4 : 3);
-
-        assert(readOnlyBufferSize > sizeof(H));
+        ImpCacheHeader* cachePtr = (ImpCacheHeader*)align_buffer(readOnlyBuffer, sizeof(pint_t) == 4 ? 4 : 3);
+
+        assert(readOnlyBufferSize > sizeof(ImpCacheHeader));
 
         uint64_t occupied = impCache.occupied();
         int64_t fallback_class_offset = *(cls->getSuperClassAddress()) - clsVMAddr;
@@ -728,27 +756,25 @@
             fallback_class_offset = superclassVMAddr - clsVMAddr;
         }
 
+        assert((int32_t)fallback_class_offset == fallback_class_offset);
         assert((uint32_t)occupied == occupied);
-        if ( cachesFormatVersion < 3 ) {
-            assert((int32_t)fallback_class_offset == fallback_class_offset);
-            cachePtr->fallback_class_offset = (int32_t)fallback_class_offset;
-        } else {
-            assert(sizeof(cachePtr->fallback_class_offset) == sizeof(fallback_class_offset));
-            cachePtr->fallback_class_offset = fallback_class_offset;
-        }
-        cachePtr->cache_shift = (uint32_t)(data->shift + 7);
-        cachePtr->cache_mask = (uint32_t)data->mask();
-        cachePtr->occupied = (uint32_t)occupied;
-        cachePtr->has_inlines = impCache.hasInlines;
-        cachePtr->bit_one = 1; // obj-c plays HORRENDOUS games here
+
+        *cachePtr = (ImpCacheHeader){
+            .fallback_class_offset = (int32_t)fallback_class_offset,
+            .cache_shift = (uint32_t)(data->shift + 7),
+            .cache_mask = (uint32_t)data->mask(),
+            .occupied = (uint32_t)occupied,
+            .has_inlines = impCache.hasInlines,
+            .bit_one = 1, // obj-c plays HORRENDOUS games here
+        };
 
         // is this right?
         int64_t vmaddr = cache->vmAddrForContent(readOnlyBuffer);
         assert((pint_t)vmaddr == (uint64_t)vmaddr);
         *vtableAddr =  (pint_t)cache->vmAddrForContent(readOnlyBuffer);
         d->_aslrTracker->add(vtableAddr);
-        readOnlyBuffer += sizeof(H);
-        readOnlyBufferSize -= sizeof(H);
+        readOnlyBuffer += sizeof(ImpCacheHeader);
+        readOnlyBufferSize -= sizeof(ImpCacheHeader);
 
         impCache.write(cache, selectorStringVMAddr, clsVMAddr, readOnlyBuffer, readOnlyBufferSize, cachesFormatVersion, diag);
     }
@@ -797,9 +823,10 @@
         : _protocolCount(0), _protocolReferenceCount(0), _diagnostics(diag), _dylibIndices(dylibIndices) {
     }
 
-    void visitCoalescedStrings(const CacheBuilder::CoalescedStringsSection& coalescedClassNames) {
-        for (const auto& stringAndOffset : coalescedClassNames.stringsToOffsets) {
-            uint64_t vmAddr = coalescedClassNames.bufferVMAddr + stringAndOffset.second;
+    void visitCoalescedStrings(const CacheBuilder::CacheCoalescedText& coalescedText) {
+        const CacheBuilder::CacheCoalescedText::StringSection& classNames = coalescedText.getSectionData("__objc_classname");
+        for (const auto& stringAndOffset : classNames.stringsToOffsets) {
+            uint64_t vmAddr = classNames.bufferVMAddr + stringAndOffset.second;
             _uniquedProtocolNames[stringAndOffset.first.data()] = vmAddr;
         }
     }
@@ -1032,7 +1059,7 @@
 #endif
 }
 
-template <typename P, typename H> static inline void emitIMPCaches(ContentAccessor& cacheAccessor,
+template <typename P> static inline void emitIMPCaches(ContentAccessor& cacheAccessor,
                                          std::vector<SharedCacheBuilder::DylibInfo> & allDylibs,
                                          std::vector<const macho_header<P>*> & sizeSortedDylibs,
                                          std::optional<uint64_t> relativeMethodListBaseAddress,
@@ -1058,8 +1085,8 @@
     timeRecorder.recordTime("compute IMP map");
     diag.verbose("[IMP caches] emitting IMP caches\n");
 
-    IMPCachesEmitter<P, H> impCachesEmitter(diag, classRecorder, selectorStringVMAddr, optROData, optRORemaining, optRWData, optRWRemaining, allDylibs, sizeSortedDylibs, objcASLRTracker, impCachesVersion);
-    ClassWalker<P, IMPCachesEmitter<P, H>> impEmitterClassWalker(impCachesEmitter, ClassWalkerMode::ClassAndMetaclasses);
+    IMPCachesEmitter<P> impCachesEmitter(diag, classRecorder, selectorStringVMAddr, optROData, optRORemaining, optRWData, optRWRemaining, allDylibs, sizeSortedDylibs, objcASLRTracker, impCachesVersion);
+    ClassWalker<P, IMPCachesEmitter<P>> impEmitterClassWalker(impCachesEmitter, ClassWalkerMode::ClassAndMetaclasses);
     for (const macho_header<P>* mh : sizeSortedDylibs) {
         impEmitterClassWalker.walk(&cacheAccessor, mh);
         if (diag.hasError())
@@ -1072,24 +1099,22 @@
 }
 
 template <typename P>
-void doOptimizeObjC(DyldSharedCache* cache, uint64_t cacheType,
-                    CacheBuilder::LOH_Tracker& lohTracker,
-                    const CacheBuilder::CoalescedStringsSection& coalescedMethodNames,
-                    const CacheBuilder::CoalescedStringsSection& coalescedClassNames,
+void doOptimizeObjC(DyldSharedCache* cache, bool forProduction,
+                    CacheBuilder::LOH_Tracker& lohTracker, const CacheBuilder::CacheCoalescedText& coalescedText,
                     const std::map<void*, std::string>& missingWeakImports, Diagnostics& diag,
                     uint8_t* objcReadOnlyBuffer, uint64_t objcReadOnlyBufferSizeUsed, uint64_t objcReadOnlyBufferSizeAllocated,
                     uint8_t* objcReadWriteBuffer, uint64_t objcReadWriteBufferSizeAllocated,
                     uint64_t objcRwFileOffset,
                     std::vector<SharedCacheBuilder::DylibInfo> & allDylibs,
                     const std::vector<const IMPCaches::Selector*> & inlinedSelectors,
-                    bool impCachesSuccess, int impCachesVersion,
+                    bool impCachesSuccess,
                     TimeRecorder& timeRecorder)
 {
     typedef typename P::E           E;
     typedef typename P::uint_t      pint_t;
 
     diag.verbose("Optimizing objc metadata:\n");
-    diag.verbose("  cache type is %s\n", DyldSharedCache::getCacheTypeName(cacheType));
+    diag.verbose("  cache type is %s\n", forProduction ? "production" : "development");
 
     ContentAccessor cacheAccessor(cache, diag);
 
@@ -1126,9 +1151,10 @@
             // state which we can access at runtime.  We do have the PBLS state today in dyld4, but its not used with
             // JIT loaders.
             const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)mh;
+            auto vmAddrConverter = ma->makeVMAddrConverter(false);
             Diagnostics diags;
             __block bool hasSwiftProtocols = false;
-            ma->forEachSwiftProtocolConformance(diags,
+            ma->forEachSwiftProtocolConformance(diags, vmAddrConverter, true,
                                                 ^(uint64_t protocolConformanceRuntimeOffset,
                                                   const dyld3::MachOAnalyzer::SwiftProtocolConformance& protocolConformance,
                                                   bool& stopProtocolConformance) {
@@ -1330,7 +1356,7 @@
     // Eventually we'll update them to offsets directly to the selector string, from the given base address
 
     SelectorOptimizer<P, ObjCSelectorUniquer<P> > selOptimizer(uniq);
-    selOptimizer.visitCoalescedStrings(coalescedMethodNames);
+    selOptimizer.visitCoalescedStrings(coalescedText);
     uint64_t relativeMethodListBaseAddress = 0;
     constexpr std::string_view magicSelector = "\xf0\x9f\xa4\xaf";
     if ( auto it = selOptimizer.strings().find(magicSelector.data()); it != selOptimizer.strings().end() ) {
@@ -1374,9 +1400,7 @@
                                                                        missingWeakImports,
                                                                        sizeSortedDylibs);
 
-    bool universalCustomer = (cacheType == kDyldSharedCacheTypeUniversal) && (cache->header.cacheSubType == kDyldSharedCacheTypeProduction);
-
-    if ( cacheType == kDyldSharedCacheTypeProduction || universalCustomer ) {
+    if (forProduction) {
         // Shared cache does not currently support unbound weak references.
         // Here we assert that there are none. If support is added later then
         // this assertion needs to be removed and this path needs to be tested.
@@ -1395,7 +1419,7 @@
     //
     // This is SAFE: the binaries themselves are unmodified.
     ClassListBuilder<P> classes(dylibIndices);
-    classes.visitCoalescedStrings(coalescedClassNames);
+    classes.visitCoalescedStrings(coalescedText);
     ClassWalker<P, ClassListBuilder<P>> classWalker(classes);
     for (const macho_header<P>* mh : sizeSortedDylibs) {
         classWalker.walk(&cacheAccessor, mh);
@@ -1435,7 +1459,7 @@
     // This must be done AFTER updating method lists.
 
     ProtocolOptimizer<P> protocolOptimizer(diag, dylibIndices);
-    protocolOptimizer.visitCoalescedStrings(coalescedClassNames);
+    protocolOptimizer.visitCoalescedStrings(coalescedText);
     for (const macho_header<P>* mh : sizeSortedDylibs) {
         protocolOptimizer.addProtocols(&cacheAccessor, mh);
     }
@@ -1513,8 +1537,9 @@
     //
     // Objc has a magic section of imp cache base pointers.  We need these to
     // offset everything else from
-    uint64_t selectorStringVMAddr = coalescedMethodNames.bufferVMAddr;
-    uint64_t selectorStringVMSize = coalescedMethodNames.bufferSize;
+    const CacheBuilder::CacheCoalescedText::StringSection& methodNames = coalescedText.getSectionData("__objc_methname");
+    uint64_t selectorStringVMAddr = methodNames.bufferVMAddr;
+    uint64_t selectorStringVMSize = methodNames.bufferSize;
     uint64_t impCachesVMSize = 0; // We'll calculate this later
 
     uint64_t optRODataRemainingBeforeImpCaches = optRORemaining;
@@ -1524,6 +1549,8 @@
     uint8_t* inlinedSelectorsStart = optRWData;
     uint8_t* inlinedSelectorsEnd = optRWData;
     
+    int impCachesVersion = 1;
+
     uint64_t pointersVMAddr = 0;
     if (optImpCachesPointerSection) {
         if (optImpCachesPointerSection->size() < sizeof(objc_opt::objc_opt_pointerlist_tt<pint_t>)) {
@@ -1535,6 +1562,7 @@
         bool found = ((dyld3::MachOAnalyzer*)libobjcMH)->findExportedSymbol(diag, "_objc_opt_preopt_caches_version", false, foundInfo, nullptr);
 
         if (found) {
+            impCachesVersion = *(int*)((uint8_t*)libobjcMH + foundInfo.value);
             found = ((dyld3::MachOAnalyzer*)libobjcMH)->findExportedSymbol(diag, "_objc_opt_offsets", false, foundInfo, nullptr);
             if (!found) {
                 diag.error("libobjc's imp cache pointer list not found (metadata not optimized)");
@@ -1542,20 +1570,15 @@
             }
             pointersVMAddr = ((dyld3::MachOAnalyzer*)libobjcMH)->preferredLoadAddress() + foundInfo.value;
         } else {
+            impCachesVersion = 1;
             pointersVMAddr = optImpCachesPointerSection->addr();
         }
     }
 
     if (impCachesSuccess) {
-        if ( impCachesVersion < 3 ) {
-            emitIMPCaches<P, ImpCacheHeader_v1>(cacheAccessor, allDylibs, sizeSortedDylibs, relativeMethodListBaseAddress,
-                             selectorStringVMAddr, optROData, optRORemaining, optRWData, optRWRemaining,
-                             *objcASLRTracker, inlinedSelectors, inlinedSelectorsStart, inlinedSelectorsEnd, impCachesVersion, diag, timeRecorder);
-        } else {
-            emitIMPCaches<P, ImpCacheHeader_v2>(cacheAccessor, allDylibs, sizeSortedDylibs, relativeMethodListBaseAddress,
-                             selectorStringVMAddr, optROData, optRORemaining, optRWData, optRWRemaining,
-                             *objcASLRTracker, inlinedSelectors, inlinedSelectorsStart, inlinedSelectorsEnd, impCachesVersion, diag, timeRecorder);
-        }
+        emitIMPCaches<P>(cacheAccessor, allDylibs, sizeSortedDylibs, relativeMethodListBaseAddress,
+                         selectorStringVMAddr, optROData, optRORemaining, optRWData, optRWRemaining,
+                         *objcASLRTracker, inlinedSelectors, inlinedSelectorsStart, inlinedSelectorsEnd, impCachesVersion, diag, timeRecorder);
     }
 
     uint8_t* alignedROData = alignPointer(optROData);
@@ -1584,7 +1607,7 @@
 
     // Collect flags.
     uint32_t headerFlags = 0;
-    if ( cacheType == kDyldSharedCacheTypeProduction || universalCustomer ) {
+    if (forProduction) {
         headerFlags |= objc_opt::IsProduction;
     }
     if (noMissingWeakSuperclasses) {
@@ -1749,16 +1772,12 @@
 
 } // anon namespace
 
-size_t IMPCaches::sizeForImpCacheWithCount(int count, int impCachesVersion) {
+size_t IMPCaches::sizeForImpCacheWithCount(int count) {
     // The architecture should not be relevant here as it's all offsets and fixed int sizes.
     // It was just the most logical place to host this function in.
 
-    size_t size64 = IMPCachesEmitter<Pointer64<LittleEndian>, ImpCacheHeader_v2>::sizeForImpCacheWithCount(count);
-    size_t size32 = IMPCachesEmitter<Pointer32<LittleEndian>, ImpCacheHeader_v2>::sizeForImpCacheWithCount(count);
-    if (impCachesVersion < 3) {
-        size64 = IMPCachesEmitter<Pointer64<LittleEndian>, ImpCacheHeader_v1>::sizeForImpCacheWithCount(count);
-        size32 = IMPCachesEmitter<Pointer32<LittleEndian>, ImpCacheHeader_v1>::sizeForImpCacheWithCount(count);
-    }
+    size_t size64 = IMPCachesEmitter<Pointer64<LittleEndian>>::sizeForImpCacheWithCount(count);
+    size_t size32 = IMPCachesEmitter<Pointer32<LittleEndian>>::sizeForImpCacheWithCount(count);
     assert(size64 == size32);
 
     return size64;
@@ -1771,32 +1790,26 @@
     // Mike suggests all relative method lists are offsets from the magic selector
     if ( _archLayout->is64 )
         doOptimizeObjC<Pointer64<LittleEndian>>(cache,
-            _options.cacheConfiguration,
+            _options.optimizeStubs,
             _lohTracker,
-            _objcCoalescedMethodNames,
-            _objcCoalescedClassNames,
+            _coalescedText,
             _missingWeakImports, _diagnostics,
             _objcReadOnlyBuffer,
             _objcReadOnlyBufferSizeUsed,
             _objcReadOnlyBufferSizeAllocated,
             _objcReadWriteBuffer, _objcReadWriteBufferSizeAllocated,
-            _objcReadWriteFileOffset, _sortedDylibs, inlinedSelectors,
-            impCachesSuccess, _impCachesBuilder->impCachesVersion,
-            _timeRecorder);
+            _objcReadWriteFileOffset, _sortedDylibs, inlinedSelectors, impCachesSuccess, _timeRecorder);
     else
         doOptimizeObjC<Pointer32<LittleEndian>>(cache,
-            _options.cacheConfiguration,
+            _options.optimizeStubs,
             _lohTracker,
-            _objcCoalescedMethodNames,
-            _objcCoalescedClassNames,
+            _coalescedText,
             _missingWeakImports, _diagnostics,
             _objcReadOnlyBuffer,
             _objcReadOnlyBufferSizeUsed,
             _objcReadOnlyBufferSizeAllocated,
             _objcReadWriteBuffer, _objcReadWriteBufferSizeAllocated,
-            _objcReadWriteFileOffset, _sortedDylibs, inlinedSelectors,
-            impCachesSuccess, _impCachesBuilder->impCachesVersion,
-            _timeRecorder);
+            _objcReadWriteFileOffset, _sortedDylibs, inlinedSelectors, impCachesSuccess, _timeRecorder);
 }
 
 static uint32_t hashTableSize(uint32_t maxElements, uint32_t perElementData)