Loading...
--- 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 {