Loading...
--- dyld/dyld-1122.1/cache_builder/NewSharedCacheBuilder.cpp
+++ dyld/dyld-1125.5/cache_builder/NewSharedCacheBuilder.cpp
@@ -112,7 +112,7 @@
}
void SharedCacheBuilder::addFile(const void* buffer, size_t bufferSize, std::string_view path,
- uint64_t inode, uint64_t modTime)
+ uint64_t inode, uint64_t modTime, bool forceNotCacheEligible)
{
Diagnostics diag;
const bool isOSBinary = false;
@@ -120,10 +120,11 @@
this->options.platform, isOSBinary,
this->options.archs) ) {
InputFile inputFile;
- inputFile.mf = mf;
- inputFile.inode = inode;
- inputFile.mtime = modTime;
- inputFile.path = path;
+ inputFile.mf = mf;
+ inputFile.inode = inode;
+ inputFile.mtime = modTime;
+ inputFile.path = path;
+ inputFile.forceNotCacheEligible = forceNotCacheEligible;
allInputFiles.push_back(std::move(inputFile));
return;
}
@@ -135,10 +136,11 @@
dyld3::Platform::iOSMac, isOSBinary,
this->options.archs) ) {
InputFile inputFile;
- inputFile.mf = mf;
- inputFile.inode = inode;
- inputFile.mtime = modTime;
- inputFile.path = path;
+ inputFile.mf = mf;
+ inputFile.inode = inode;
+ inputFile.mtime = modTime;
+ inputFile.path = path;
+ inputFile.forceNotCacheEligible = forceNotCacheEligible;
allInputFiles.push_back(std::move(inputFile));
return;
}
@@ -517,7 +519,7 @@
}
}
- if ( inputFile.mf->canBePlacedInDyldCache(dylibPath.data(), failureHandler) ) {
+ if ( !inputFile.forceNotCacheEligible && inputFile.mf->canBePlacedInDyldCache(dylibPath.data(), failureHandler) ) {
CacheDylib cacheDylib(inputFile);
this->cacheDylibs.push_back(std::move(cacheDylib));
}
@@ -978,6 +980,27 @@
// diag.warning("libobjc's magical IMP caches shared cache offsets list section missing (metadata not optimized)");
return;
}
+
+ // Also find the _objc_opt_preopt_caches_version symbol, which has the IMP caches version
+ __block Diagnostics diag;
+ cacheDylib.inputMF->withFileLayout(diag, ^(const mach_o::Layout &layout) {
+ mach_o::Layout::FoundSymbol foundInfo;
+ if ( !layout.findExportedSymbol(diag, "_objc_opt_preopt_caches_version", false, foundInfo) )
+ return;
+
+ // We only support header offsets in this dylib, as we are looking for self binds
+ // which are likely only to classes
+ if ( foundInfo.kind != mach_o::Layout::FoundSymbol::Kind::headerOffset )
+ return;
+
+ uint64_t vmAddr = layout.textUnslidVMAddr() + foundInfo.value;
+
+ __block objc_visitor::Visitor objcVisitor = makeInputDylibObjCVisitor(cacheDylib);
+ metadata_visitor::ResolvedValue value = objcVisitor.getValueFor(VMAddress(vmAddr));
+ this->objcIMPCachesOptimizer.libobjcImpCachesVersion = *(int*)value.value();
+ });
+ if ( diag.hasError() )
+ return;
}
// Find all the objc dylibs, classes, categories
@@ -4107,12 +4130,6 @@
assert(this->totalVMSize == totalCustomerCacheSize);
}
- if ( this->totalVMSize > this->config.layout.cacheSize ) {
- return Error("Cache overflow (0x%llx > 0x%llx)",
- this->totalVMSize.rawValue(),
- this->config.layout.cacheSize.rawValue());
- }
-
return Error();
}
@@ -4140,6 +4157,7 @@
// Check for overflow
if ( region.subCacheVMSize > this->config.layout.discontiguous->simTextSize ) {
+ evictLeafDylibs(region.subCacheVMSize - this->config.layout.discontiguous->simTextSize);
return Error("Overflow in text (0x%llx > 0x%llx)",
region.subCacheVMSize.rawValue(),
this->config.layout.discontiguous->simTextSize.rawValue());
@@ -4296,14 +4314,105 @@
}
this->totalVMSize = CacheVMSize((vmAddress - this->config.layout.cacheBaseAddress).rawValue());
+
+ return Error();
+}
+
+void SharedCacheBuilder::evictLeafDylibs(CacheVMSize reductionTarget)
+{
+ // build a reverse map of all dylib dependencies
+ std::unordered_map<std::string_view, std::unordered_set<std::string_view>> references;
+ // Ensure we have an entry (even if it is empty)
+ for ( const CacheDylib& cacheDylib : cacheDylibs )
+ references[cacheDylib.installName] = { };
- if ( this->totalVMSize > this->config.layout.cacheSize ) {
- return Error("Cache overflow (0x%llx > 0x%llx)",
- this->totalVMSize.rawValue(),
- this->config.layout.cacheSize.rawValue());
- }
-
- return Error();
+ for ( const CacheDylib& cacheDylib : cacheDylibs ) {
+ for ( const CacheDylib::DependentDylib& depDylib : cacheDylib.dependents ) {
+ // Skip missing weak links
+ if ( depDylib.dylib == nullptr )
+ continue;
+ references[depDylib.dylib->installName].insert(cacheDylib.installName);
+ }
+ }
+
+ struct DylibAndSize
+ {
+ CacheDylib* dylib;
+ CacheVMSize size;
+ };
+
+ // Find the sizes of all the dylibs
+ std::vector<DylibAndSize> dylibsToSort;
+ for ( CacheDylib& cacheDylib : cacheDylibs ) {
+ CacheVMSize segsSize = CacheVMSize(0ULL);
+ for ( const DylibSegmentChunk& segment : cacheDylib.segments ) {
+ if ( segment.segmentName == "__LINKEDIT" )
+ continue;
+
+ segsSize += segment.cacheVMSize;
+ }
+ dylibsToSort.push_back({ &cacheDylib, segsSize });
+ }
+
+ // Build an ordered list of what to remove. At each step we do following
+ // 1) Find all dylibs that nothing else depends on
+ // 2a) If any of those dylibs are not in the order select the largest one of them
+ // 2b) If all the leaf dylibs are in the order file select the last dylib that appears last in the order file
+ // 3) Remove all entries to the removed file from the reverse dependency map
+ // 4) Go back to one and repeat until there are no more evictable dylibs
+ // This results in us always choosing the locally optimal selection, and then taking into account how that impacts
+ // the dependency graph for subsequent selections
+
+ std::vector<DylibAndSize> sortedDylibs;
+ bool candidateFound = true;
+ while ( candidateFound ) {
+ candidateFound = false;
+ DylibAndSize candidate;
+ uint64_t candidateOrder = 0;
+ for( const auto& dylib : dylibsToSort ) {
+ const auto& dylibRefs = references.at(dylib.dylib->installName);
+ if ( !dylibRefs.empty())
+ continue;
+
+ const auto& j = options.dylibOrdering.find(std::string(dylib.dylib->installName));
+ uint64_t order = 0;
+ if ( j != options.dylibOrdering.end() ) {
+ order = j->second;
+ } else {
+ // Not in the order file, set order sot it goes to the front of the list
+ order = UINT64_MAX;
+ }
+ if ( order > candidateOrder || (order == UINT64_MAX && candidate.size < dylib.size) ) {
+ // The new file is either a lower priority in the order file
+ // or the same priority as the candidate but larger
+ candidate = dylib;
+ candidateOrder = order;
+ candidateFound = true;
+ }
+ }
+ if (candidateFound) {
+ sortedDylibs.push_back(candidate);
+ references.erase(candidate.dylib->installName);
+ for (auto& dependent : references) {
+ (void)dependent.second.erase(candidate.dylib->installName);
+ }
+ auto j = std::find_if(dylibsToSort.begin(), dylibsToSort.end(),
+ [&candidate](const DylibAndSize& dylib) {
+ return candidate.dylib->installName == dylib.dylib->installName;
+ });
+ if ( j != dylibsToSort.end() ) {
+ dylibsToSort.erase(j);
+ }
+ }
+ }
+
+ // build set of dylibs that if removed will allow cache to build
+ for ( DylibAndSize& dylib : sortedDylibs ) {
+ this->evictedDylibs.push_back(dylib.dylib->inputFile->path);
+ if ( dylib.size > reductionTarget )
+ break;
+ reductionTarget -= dylib.size;
+ }
}
// In file layout, we need each Region to start page-aligned. Within a Region, we can pack pages
@@ -4390,6 +4499,13 @@
return error;
}
}
+
+ if ( this->totalVMSize > this->config.layout.cacheSize ) {
+ evictLeafDylibs(this->totalVMSize - this->config.layout.cacheSize);
+ return Error("Cache overflow (0x%llx > 0x%llx)",
+ this->totalVMSize.rawValue(),
+ this->config.layout.cacheSize.rawValue());
+ }
// Update Section VMAddr's now that we know where all the Region's are in memory
for ( SubCache& subCache : this->subCaches ) {
@@ -4403,12 +4519,6 @@
}
}
}
- }
-
- if ( this->totalVMSize > this->config.layout.cacheSize ) {
- return Error("Cache overflow (0x%llx > 0x%llx)",
- this->totalVMSize.rawValue(),
- this->config.layout.cacheSize.rawValue());
}
return Error();
@@ -6442,7 +6552,7 @@
Timer::Scope timedScope(this->config, "emitCanonicalObjCProtocols time");
- const bool log = this->options.debug;
+ const bool log = false;
// We need to find the Protocol class from libojc
VMAddress protocolClassVMAddr;
@@ -6618,7 +6728,7 @@
Timer::Scope timedScope(this->config, "computeObjCClassLayout time");
- const bool log = this->options.debug;
+ const bool log = false;
// We need to walk all classes in all dylibs. Each dylib needs its own objc visitor object
std::vector<objc_visitor::Visitor> objcVisitors;
@@ -7484,6 +7594,11 @@
return buff;
}
+std::span<const std::string_view> SharedCacheBuilder::getEvictedDylibs() const
+{
+ return this->evictedDylibs;
+}
+
void SharedCacheBuilder::getResults(std::vector<CacheBuffer>& results) const
{
for ( const SubCache& subCache : this->subCaches ) {