Loading...
dyld3/shared-cache/OptimizerLinkedit.cpp dyld-750.6 dyld-733.6
--- dyld/dyld-750.6/dyld3/shared-cache/OptimizerLinkedit.cpp
+++ dyld/dyld-733.6/dyld3/shared-cache/OptimizerLinkedit.cpp
@@ -183,6 +183,341 @@
     uint32_t                                _newIndirectSymbolTableOffset   = 0;
     uint64_t                                _dyldSectionAddr                = 0;
 };
+
+
+
+template <typename P>
+class AcceleratorTables {
+public:
+                AcceleratorTables(DyldSharedCache* cache, uint64_t linkeditStartAddr, Diagnostics& diag, const std::vector<LinkeditOptimizer<P>*>& optimizers);
+
+    uint32_t    totalSize() const;
+    void        copyTo(uint8_t* buffer);
+
+private:
+    typedef typename P::E  E;
+
+    struct NodeChain;
+
+    struct DepNode {
+        std::vector<DepNode*>   _dependents;
+        unsigned                _depth;
+        const char*             _installName;
+
+                                DepNode() : _depth(0), _installName(nullptr) { }
+        void                    computeDepth();
+        static void             verifyUnreachable(DepNode* target, NodeChain& chain, Diagnostics& diag, std::unordered_set<DepNode*>& visitedNodes, const std::vector<DepNode*>& from);
+    };
+
+    struct NodeChain {
+        NodeChain*   prev;
+        DepNode*     node;
+    };
+
+    std::unordered_map<macho_header<P>*, DepNode>                   _depDAG;
+    std::vector<dyld_cache_image_info_extra>                        _extraInfo;
+    std::vector<uint8_t>                                            _trieBytes;
+    std::vector<uint16_t>                                           _reExportArray;
+    std::vector<uint16_t>                                           _dependencyArray;
+    std::vector<uint16_t>                                           _bottomUpArray;
+    std::vector<dyld_cache_accelerator_initializer>                 _initializers;
+    std::vector<dyld_cache_accelerator_dof>                         _dofSections;
+    std::vector<dyld_cache_range_entry>                             _rangeTable;
+    std::unordered_map<macho_header<P>*, uint32_t>                  _machHeaderToImageIndex;
+    std::unordered_map<std::string, macho_header<P>*>               _dylibPathToMachHeader;
+    std::unordered_map<macho_header<P>*, LinkeditOptimizer<P>*>     _machHeaderToOptimizer;
+    dyld_cache_accelerator_info                                     _acceleratorInfoHeader;
+};
+
+
+template <typename P>
+void AcceleratorTables<P>::AcceleratorTables::DepNode::verifyUnreachable(AcceleratorTables<P>::DepNode* target, struct AcceleratorTables<P>::NodeChain& chain, Diagnostics& diag,
+                                                                        std::unordered_set<DepNode*>& visitedNodes, const std::vector<AcceleratorTables<P>::DepNode*>& from) {
+    for (DepNode* node : from) {
+        bool foundCycle = (node == target);
+        for (NodeChain* c = &chain; c->prev != nullptr; c = c->prev) {
+            if ( c->node == target ) {
+                foundCycle = true;
+                break;
+            }
+        }
+        if ( foundCycle ) {
+            NodeChain* chp = &chain;
+            std::string msg = std::string("found cycle for ") + target->_installName;
+            while (chp != nullptr) {
+                msg = msg + "\n  " + chp->node->_installName;
+                chp = chp->prev;
+            }
+            diag.warning("%s", msg.c_str());
+            return;
+        }
+
+        if ( visitedNodes.count(node) )
+            continue;
+        visitedNodes.insert(node);
+        NodeChain nextChain;
+        nextChain.prev = &chain;
+        nextChain.node = node;
+        verifyUnreachable(target, nextChain, diag, visitedNodes, node->_dependents);
+    }
+}
+
+const uint16_t kBranchIslandDylibIndex = 0x7FFF;
+
+template <typename P>
+AcceleratorTables<P>::AcceleratorTables(DyldSharedCache* cache, uint64_t linkeditStartAddr, Diagnostics& diag, const std::vector<LinkeditOptimizer<P>*>& optimizers)
+{
+    // build table mapping tables to map between mach_header, index, and optimizer
+    for ( LinkeditOptimizer<P>* op : optimizers ) {
+        _machHeaderToOptimizer[op->machHeader()] = op;
+    }
+    const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((uint8_t*)cache + cache->header.mappingOffset);
+    uint64_t cacheStartAddress = mappings[0].address;
+    const dyld_cache_image_info* images = (dyld_cache_image_info*)((uint8_t*)cache + cache->header.imagesOffset);
+    for (unsigned i=0; i < cache->header.imagesCount; ++i) {
+        uint64_t segCacheFileOffset = images[i].address - cacheStartAddress;
+        macho_header<P>* mhMapped = (macho_header<P>*)((uint8_t*)cache+segCacheFileOffset);
+        const char* path = (char*)cache + images[i].pathFileOffset;
+        _dylibPathToMachHeader[path] = mhMapped;
+        // don't add alias entries (path offset in pool near start of cache) to header->index map
+        if ( images[i].pathFileOffset > segCacheFileOffset )
+            _machHeaderToImageIndex[mhMapped] = i;
+    }
+
+
+    // build DAG of image dependencies
+    for (LinkeditOptimizer<P>* op : optimizers) {
+        _depDAG[op->machHeader()]._installName = op->installName();
+    }
+    for (LinkeditOptimizer<P>* op : optimizers) {
+        DepNode& node = _depDAG[op->machHeader()];
+        for (const char* depPath : op->getDownwardDependents()) {
+            macho_header<P>* depMH = _dylibPathToMachHeader[depPath];
+            if ( depMH != nullptr ) {
+                DepNode* depNode = &_depDAG[depMH];
+                node._dependents.push_back(depNode);
+            }
+        }
+    }
+
+    // check for cycles in DAG
+    for (auto& entry : _depDAG) {
+        DepNode* node = &entry.second;
+        NodeChain chain;
+        chain.prev = nullptr;
+        chain.node = node;
+        std::unordered_set<DepNode*> visitedNodes;
+        DepNode::verifyUnreachable(node, chain, diag, visitedNodes, node->_dependents);
+    }
+
+    // compute depth for each DAG node
+    for (auto& entry : _depDAG) {
+        entry.second.computeDepth();
+    }
+
+    // build sorted (bottom up) list of images
+    std::vector<macho_header<P>*> sortedMachHeaders;
+    sortedMachHeaders.reserve(optimizers.size());
+    for (LinkeditOptimizer<P>* op : optimizers) {
+        if ( strcmp(op->installName(), "dyld_shared_cache_branch_islands") != 0 )
+            sortedMachHeaders.push_back(op->machHeader());
+        else
+            _machHeaderToImageIndex[op->machHeader()] = kBranchIslandDylibIndex;
+    }
+    std::sort(sortedMachHeaders.begin(), sortedMachHeaders.end(),
+              [&](macho_header<P>* lmh, macho_header<P>* rmh) -> bool {
+                if ( _depDAG[lmh]._depth != _depDAG[rmh]._depth )
+                    return (_depDAG[lmh]._depth < _depDAG[rmh]._depth);
+                else
+                    return (lmh < rmh);
+              });
+
+    // build zeroed array of extra infos
+    dyld_cache_image_info_extra emptyExtra;
+    emptyExtra.exportsTrieAddr              = 0;
+    emptyExtra.weakBindingsAddr             = 0;
+    emptyExtra.exportsTrieSize              = 0;
+    emptyExtra.weakBindingsSize             = 0;
+    emptyExtra.dependentsStartArrayIndex    = 0;
+    emptyExtra.reExportsStartArrayIndex     = 0;
+    _extraInfo.insert(_extraInfo.begin(), sortedMachHeaders.size(), emptyExtra);
+
+    //for ( macho_header<P>* mh : sortedMachHeaders ) {
+    //    fprintf(stderr, "depth: %3d mh: %p  path: %s\n", _depDAG[mh]._depth, mh, _machHeaderToOptimizer[mh]->installName());
+    //}
+
+    // build dependency table
+    _dependencyArray.push_back(0xFFFF); // reserve 0 slot to be "no-dependencies"
+    for (macho_header<P>* mh : sortedMachHeaders) {
+        LinkeditOptimizer<P>* op = _machHeaderToOptimizer[mh];
+        unsigned index = _machHeaderToImageIndex[mh];
+        auto depPaths = op->getAllDependents();
+        if ( depPaths.empty() ) {
+            _extraInfo[index].dependentsStartArrayIndex = 0;
+        }
+        else {
+            _extraInfo[index].dependentsStartArrayIndex = (uint32_t)_dependencyArray.size();
+            auto downPaths = op->getDownwardDependents();
+            for (const char* depPath : depPaths) {
+                macho_header<P>* depMH = _dylibPathToMachHeader[depPath];
+                uint16_t depIndex = _machHeaderToImageIndex[depMH];
+                if ( std::find(downPaths.begin(), downPaths.end(), depPath) == downPaths.end())
+                    depIndex |= 0x8000;
+                _dependencyArray.push_back(depIndex);
+            }
+            _dependencyArray.push_back(0xFFFF); // mark end of list
+       }
+    }
+
+    // build re-exports table
+    _reExportArray.push_back(0xFFFF); // reserve 0 slot to be "no-re-exports"
+    for (macho_header<P>* mh : sortedMachHeaders) {
+        LinkeditOptimizer<P>* op = _machHeaderToOptimizer[mh];
+        unsigned index = _machHeaderToImageIndex[mh];
+        auto reExPaths = op->getReExportPaths();
+        if ( reExPaths.empty() ) {
+            _extraInfo[index].reExportsStartArrayIndex = 0;
+        }
+        else {
+            _extraInfo[index].reExportsStartArrayIndex = (uint32_t)_reExportArray.size();
+            for (const char* reExPath : reExPaths) {
+                macho_header<P>* reExMH = _dylibPathToMachHeader[reExPath];
+                uint32_t reExIndex = _machHeaderToImageIndex[reExMH];
+                _reExportArray.push_back(reExIndex);
+            }
+            _reExportArray.push_back(0xFFFF); // mark end of list
+       }
+    }
+
+    // build ordered list of initializers
+    for (macho_header<P>* mh : sortedMachHeaders) {
+        LinkeditOptimizer<P>* op = _machHeaderToOptimizer[mh];
+        unsigned index = _machHeaderToImageIndex[mh];
+        _bottomUpArray.push_back(index);
+        for (uint64_t initializer : op->initializerAddresses()) {
+            //fprintf(stderr, "0x%08llX %s\n", initializer, op->installName());
+            dyld_cache_accelerator_initializer entry;
+            entry.functionOffset = (uint32_t)(initializer-cacheStartAddress);
+            entry.imageIndex = _machHeaderToImageIndex[mh];
+            _initializers.push_back(entry);
+        }
+    }
+
+    // build ordered list of DOF sections
+    for (macho_header<P>* mh : sortedMachHeaders) {
+        LinkeditOptimizer<P>* op = _machHeaderToOptimizer[mh];
+        assert(op != NULL);
+        unsigned imageIndex = _machHeaderToImageIndex[mh];
+        for (auto& sect : op->dofSections()) {
+            //fprintf(stderr, "0x%08llX %s\n", initializer, op->installName());
+            dyld_cache_accelerator_dof entry;
+            entry.sectionAddress = sect->addr();
+            entry.sectionSize    = (uint32_t)sect->size();
+            entry.imageIndex     = imageIndex;
+            _dofSections.push_back(entry);
+        }
+    }
+
+
+    // register exports trie and weak binding info in each dylib with image extra info
+    for (macho_header<P>* mh : sortedMachHeaders) {
+        LinkeditOptimizer<P>* op = _machHeaderToOptimizer[mh];
+        unsigned index = _machHeaderToImageIndex[mh];
+        _extraInfo[index].exportsTrieAddr  = op->exportsTrieLinkEditOffset() + linkeditStartAddr;
+        _extraInfo[index].exportsTrieSize  = op->exportsTrieLinkEditSize();
+        _extraInfo[index].weakBindingsAddr = op->weakBindingLinkEditOffset() + linkeditStartAddr;
+        _extraInfo[index].weakBindingsSize = op->weakBindingLinkEditSize();
+    }
+
+    // record location of __DATA/__dyld section in libdyld.dylib
+    macho_header<P>* libdyldMH = _dylibPathToMachHeader["/usr/lib/system/libdyld.dylib"];
+    LinkeditOptimizer<P>* libdyldOp = _machHeaderToOptimizer[libdyldMH];
+    uint64_t dyldSectionAddr = libdyldOp->dyldSectionAddress();
+
+    // build range table for fast address->image lookups
+    for (macho_header<P>* mh : sortedMachHeaders) {
+        LinkeditOptimizer<P>* op = _machHeaderToOptimizer[mh];
+        unsigned imageIndex = _machHeaderToImageIndex[mh];
+        for (const macho_segment_command<P>* segCmd : op->segCmds()) {
+            if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 )
+                continue;
+            dyld_cache_range_entry entry;
+            entry.startAddress = segCmd->vmaddr();
+            entry.size         = (uint32_t)segCmd->vmsize();
+            entry.imageIndex   = imageIndex;
+            _rangeTable.push_back(entry);
+        }
+    }
+    std::sort(_rangeTable.begin(), _rangeTable.end(),
+              [&](const dyld_cache_range_entry& lRange, const dyld_cache_range_entry& rRange) -> bool {
+                return (lRange.startAddress < rRange.startAddress);
+              });
+
+    // build trie that maps install names to image index
+    std::vector<DylibIndexTrie::Entry> dylibEntrys;
+    for (auto &x : _dylibPathToMachHeader) {
+        const std::string& path = x.first;
+        unsigned index = _machHeaderToImageIndex[x.second];
+        dylibEntrys.push_back(DylibIndexTrie::Entry(path, DylibIndex(index)));
+    }
+    DylibIndexTrie dylibsTrie(dylibEntrys);
+    dylibsTrie.emit(_trieBytes);
+    while ( (_trieBytes.size() % 4) != 0 )
+        _trieBytes.push_back(0);
+
+    // fill out header
+    _acceleratorInfoHeader.version              = 1;
+    _acceleratorInfoHeader.imageExtrasCount     = (uint32_t)_extraInfo.size();
+    _acceleratorInfoHeader.imagesExtrasOffset   = ALIGN_AS_TYPE(sizeof(dyld_cache_accelerator_info), dyld_cache_image_info_extra);
+    _acceleratorInfoHeader.bottomUpListOffset   = _acceleratorInfoHeader.imagesExtrasOffset + _acceleratorInfoHeader.imageExtrasCount*sizeof(dyld_cache_image_info_extra);
+    _acceleratorInfoHeader.dylibTrieOffset      = _acceleratorInfoHeader.bottomUpListOffset + _acceleratorInfoHeader.imageExtrasCount*sizeof(uint16_t);
+    _acceleratorInfoHeader.dylibTrieSize        = (uint32_t)_trieBytes.size();
+    _acceleratorInfoHeader.initializersOffset   = ALIGN_AS_TYPE(_acceleratorInfoHeader.dylibTrieOffset + _acceleratorInfoHeader.dylibTrieSize, dyld_cache_accelerator_initializer);
+    _acceleratorInfoHeader.initializersCount    = (uint32_t)_initializers.size();
+    _acceleratorInfoHeader.dofSectionsOffset    = ALIGN_AS_TYPE(_acceleratorInfoHeader.initializersOffset + _acceleratorInfoHeader.initializersCount*sizeof(dyld_cache_accelerator_initializer), dyld_cache_accelerator_initializer);
+    _acceleratorInfoHeader.dofSectionsCount     = (uint32_t)_dofSections.size();
+    _acceleratorInfoHeader.reExportListOffset   = ALIGN_AS_TYPE(_acceleratorInfoHeader.dofSectionsOffset + _acceleratorInfoHeader.dofSectionsCount*sizeof(dyld_cache_accelerator_dof), dyld_cache_accelerator_dof);
+    _acceleratorInfoHeader.reExportCount        = (uint32_t)_reExportArray.size();
+    _acceleratorInfoHeader.depListOffset        = ALIGN_AS_TYPE(_acceleratorInfoHeader.reExportListOffset + _acceleratorInfoHeader.reExportCount*sizeof(uint16_t), uint16_t);
+    _acceleratorInfoHeader.depListCount         = (uint32_t)_dependencyArray.size();
+    _acceleratorInfoHeader.rangeTableOffset     = ALIGN_AS_TYPE(_acceleratorInfoHeader.depListOffset + _acceleratorInfoHeader.depListCount*sizeof(uint16_t), dyld_cache_range_entry);
+    _acceleratorInfoHeader.rangeTableCount      = (uint32_t)_rangeTable.size();
+    _acceleratorInfoHeader.dyldSectionAddr      = dyldSectionAddr;
+}
+
+
+template <typename P>
+void AcceleratorTables<P>::DepNode::computeDepth()
+{
+    if ( _depth != 0 )
+        return;
+    _depth = 1;
+    for (DepNode* node : _dependents) {
+        node->computeDepth();
+        if ( node->_depth >= _depth )
+            _depth = node->_depth + 1;
+    }
+}
+
+template <typename P>
+uint32_t AcceleratorTables<P>::totalSize() const
+{
+    return (uint32_t)align(_acceleratorInfoHeader.rangeTableOffset + _acceleratorInfoHeader.rangeTableCount*sizeof(dyld_cache_range_entry), 14);
+}
+
+template <typename P>
+void AcceleratorTables<P>::copyTo(uint8_t* buffer)
+{
+    memcpy(buffer, &_acceleratorInfoHeader, sizeof(dyld_cache_accelerator_info));
+    memcpy(&buffer[_acceleratorInfoHeader.imagesExtrasOffset], &_extraInfo[0],       _extraInfo.size()*sizeof(dyld_cache_image_info_extra));
+    memcpy(&buffer[_acceleratorInfoHeader.bottomUpListOffset], &_bottomUpArray[0],   _bottomUpArray.size()*sizeof(uint16_t));
+    memcpy(&buffer[_acceleratorInfoHeader.initializersOffset], &_initializers[0],    _initializers.size()*sizeof(dyld_cache_accelerator_initializer));
+    memcpy(&buffer[_acceleratorInfoHeader.reExportListOffset], &_reExportArray[0],   _reExportArray.size()*sizeof(uint16_t));
+    memcpy(&buffer[_acceleratorInfoHeader.dofSectionsOffset],  &_dofSections[0],     _dofSections.size()*sizeof(dyld_cache_accelerator_dof));
+    memcpy(&buffer[_acceleratorInfoHeader.depListOffset],      &_dependencyArray[0], _dependencyArray.size()*sizeof(uint16_t));
+    memcpy(&buffer[_acceleratorInfoHeader.rangeTableOffset],   &_rangeTable[0],      _rangeTable.size()*sizeof(dyld_cache_range_entry));
+    memcpy(&buffer[_acceleratorInfoHeader.dylibTrieOffset],    &_trieBytes[0],       _trieBytes.size());
+}
 
 
 
@@ -286,7 +621,6 @@
                     }
                 }
                 break;
-            case LC_DYLD_CHAINED_FIXUPS:
             case LC_SEGMENT_SPLIT_INFO:
                 remove = true;
                 break;
@@ -755,6 +1089,25 @@
     ::free(newLinkEdit);
     builder._readOnlyRegion.sizeInUse = builder._nonLinkEditReadOnlySize + newLinkeditAlignedSize;
 
+    // If making cache for customers, add extra accelerator tables for dyld
+    DyldSharedCache* cacheHeader = (DyldSharedCache*)builder._readExecuteRegion.buffer;
+    if ( builder._options.optimizeStubs ) {
+        uint64_t addrWhereAccTablesWillBe     = builder._readOnlyRegion.unslidLoadAddress+builder._readOnlyRegion.sizeInUse;
+        uint64_t addrWhereMergedLinkWillStart = builder._readOnlyRegion.unslidLoadAddress+builder._nonLinkEditReadOnlySize;
+        AcceleratorTables<P> tables(cacheHeader, addrWhereMergedLinkWillStart, builder._diagnostics, optimizers);
+        uint32_t tablesSize = tables.totalSize();
+        if ( tablesSize < (builder._readOnlyRegion.bufferSize - builder._readOnlyRegion.sizeInUse) ) {
+            tables.copyTo(builder._readOnlyRegion.buffer+builder._readOnlyRegion.sizeInUse);
+            cacheHeader->header.accelerateInfoAddr = addrWhereAccTablesWillBe;
+            cacheHeader->header.accelerateInfoSize = tablesSize;
+            builder._readOnlyRegion.sizeInUse += align(tablesSize, 14);
+            builder._diagnostics.verbose("Accelerator tables %uMB\n", (uint32_t)tablesSize/(1024*1024));
+       }
+        else {
+            builder._diagnostics.warning("not enough room to add dyld accelerator tables");
+        }
+    }
+
     // overwrite end of un-opt linkedits to create a new unmapped region for local symbols
     if ( builder._options.excludeLocalSymbols ) {
         const uint32_t entriesOffset = sizeof(dyld_cache_local_symbols_info);
@@ -788,7 +1141,6 @@
             // copy string pool
             localSymbolsStringPool.copyPoolAndUpdateOffsets(((char*)infoHeader)+stringsOffset, newLocalsSymbolTable);
             // update cache header
-            DyldSharedCache* cacheHeader = (DyldSharedCache*)builder._readExecuteRegion.buffer;
             cacheHeader->header.localSymbolsSize    = localsBufferSize;
             // return buffer of local symbols, caller to free() it
             builder._localSymbolsRegion.buffer      = (uint8_t*)localsBuffer;
@@ -837,7 +1189,7 @@
 
 void CacheBuilder::optimizeLinkedit()
 {
-    if ( _is64 ) {
+    if ( _archLayout->is64 ) {
         return LinkeditOptimizer<Pointer64<LittleEndian>>::optimizeLinkedit(*this);
     }
     else {