Loading...
--- dyld/dyld-1235.2/cache_builder/NewSharedCacheBuilder.cpp
+++ dyld/dyld-1066.8/cache_builder/NewSharedCacheBuilder.cpp
@@ -24,7 +24,6 @@
#include "Defines.h"
#include "NewSharedCacheBuilder.h"
-#include "MachOFile.h"
#include "NewAdjustDylibSegments.h"
#include "CacheDylib.h"
#include "ClosureFileSystem.h"
@@ -37,17 +36,12 @@
#include "ObjCVisitor.h"
#include "Trie.hpp"
#include "JustInTimeLoader.h"
-#include "OptimizerObjC.h"
#include "OptimizerSwift.h"
#include "PrebuiltLoader.h"
#include "DyldProcessConfig.h"
#include "DyldRuntimeState.h"
#include "SwiftVisitor.h"
#include "ParallelUtils.h"
-#include "CString.h"
-#include "Version32.h"
-#include "ExternalGenericMetadataBuilderImport.h"
-#include <SharedCacheLinker/SharedCacheLinker.h>
// FIXME: Remove this once we don't write to the old objc header struct. See emitObjCOptsHeader()
#include "objc-shared-cache.h"
@@ -56,8 +50,6 @@
#include <list>
#include <mach-o/nlist.h>
#include <sstream>
-#include <sys/fcntl.h>
-#include <sys/stat.h>
#include <unordered_set>
using dyld3::GradedArchs;
@@ -69,9 +61,8 @@
using dyld4::ProcessConfig;
using dyld4::RuntimeState;
using dyld4::SyscallDelegate;
-using dyld4::RuntimeLocks;
-
-using lsl::Allocator;
+
+using lsl::EphemeralAllocator;
using metadata_visitor::SwiftConformance;
using metadata_visitor::SwiftVisitor;
@@ -102,21 +93,9 @@
}
}
-void SharedCacheBuilder::forEachError(void (^callback)(const std::string_view& str)) const
-{
- for ( const std::string& str : this->errors ) {
- callback(str);
- }
-}
-
void SharedCacheBuilder::forEachCacheDylib(void (^callback)(const std::string_view& path)) const
{
for ( const CacheDylib& cacheDylib : this->cacheDylibs ) {
- // skip Swift prespecialized dylib if it's been built
- // it's synthesized by the builder, so mrm doesn't need to remove it
- if ( swiftPrespecializedDylib && &cacheDylib == swiftPrespecializedDylib )
- continue;
-
// Note this has to return the path, not the install name, as MRM uses this to delete
// the path from disk
callback(cacheDylib.inputFile->path);
@@ -131,21 +110,18 @@
}
void SharedCacheBuilder::addFile(const void* buffer, size_t bufferSize, std::string_view path,
- uint64_t inode, uint64_t modTime, bool forceNotCacheEligible)
+ uint64_t inode, uint64_t modTime)
{
Diagnostics diag;
const bool isOSBinary = false;
- uint64_t sliceLen = 0;
- if ( const MachOFile* mf = MachOFile::compatibleSlice(diag, sliceLen, buffer, bufferSize, path.data(),
+ if ( const MachOFile* mf = MachOFile::compatibleSlice(diag, buffer, bufferSize, path.data(),
this->options.platform, isOSBinary,
this->options.archs) ) {
InputFile inputFile;
- inputFile.mf = mf;
- inputFile.inode = inode;
- inputFile.mtime = modTime;
- inputFile.size = sliceLen;
- inputFile.path = path;
- inputFile.forceNotCacheEligible = forceNotCacheEligible;
+ inputFile.mf = mf;
+ inputFile.inode = inode;
+ inputFile.mtime = modTime;
+ inputFile.path = path;
allInputFiles.push_back(std::move(inputFile));
return;
}
@@ -153,16 +129,14 @@
// On macOS, also allow iOSMac dylibs
if ( this->options.platform == dyld3::Platform::macOS ) {
diag.clearError();
- if ( const MachOFile* mf = MachOFile::compatibleSlice(diag, sliceLen, buffer, bufferSize, path.data(),
+ if ( const MachOFile* mf = MachOFile::compatibleSlice(diag, buffer, bufferSize, path.data(),
dyld3::Platform::iOSMac, isOSBinary,
this->options.archs) ) {
InputFile inputFile;
- inputFile.mf = mf;
- inputFile.inode = inode;
- inputFile.mtime = modTime;
- inputFile.size = sliceLen;
- inputFile.path = path;
- inputFile.forceNotCacheEligible = forceNotCacheEligible;
+ inputFile.mf = mf;
+ inputFile.inode = inode;
+ inputFile.mtime = modTime;
+ inputFile.path = path;
allInputFiles.push_back(std::move(inputFile));
return;
}
@@ -189,9 +163,6 @@
if ( this->allInputFiles.empty() )
return Error("Cannot build cache with no inputs");
- // Reserve a slot for the Swift prespecialized dylib early, so that it can be ordered
- this->reserveSwiftPrespecializedInputFile();
-
this->categorizeInputs();
this->verifySelfContained();
@@ -200,22 +171,10 @@
this->sortDylibs();
- // Note this needs to be after sorting, so the order of objc dylibs is consistent with all dylibs list
- this->findObjCDylibs();
-
- // ObjC dylibs order is now set, so we can create the Swift prespecialized dylib
- // Note this needs to happen after order is known because the Swift dylib needs to
- // known indices of other shared cache dylibs. To create the dylib earlier we would need
- // to add split seg support for dylib indices.
- if ( Error error = this->createSwiftPrespecializedDylib() ) {
- swiftPrespecializedDylibBuildError = error.message();
- return error;
- }
-
// Note this needs to be after sorting, as aliases point to the cache dylibs
this->calculateDylibAliases();
- if ( Error error = this->calculateDylibDependents() )
+ if ( Error error = this->calculateDylibDependents(); error.hasError() )
return error;
this->categorizeDylibSegments();
@@ -232,15 +191,14 @@
Error SharedCacheBuilder::estimateGlobalOptimizations()
{
this->estimateIMPCaches();
+ this->findObjCDylibs();
this->findCanonicalObjCSelectors();
this->findCanonicalObjCClassNames();
this->findCanonicalObjCProtocolNames();
this->findObjCClasses();
this->findObjCProtocols();
- this->findObjCCategories();
this->estimateObjCHashTableSizes();
this->calculateObjCCanonicalProtocolsSize();
- this->calculateObjCCategoriesSize();
// Note, swift hash tables depends on findObjCClasses()
this->estimateSwiftHashTableSizes();
@@ -280,218 +238,8 @@
return Error();
}
-// This name is used only to create a placeholder input file and determine the library order.
-const std::string_view swiftPrespecializedDylibInstallName = "/usr/lib/libswiftPrespecialized.dylib";
-
-bool SharedCacheBuilder::shouldBuildSwiftPrespecializedDylib()
-{
- if ( options.platform == dyld3::Platform::driverKit )
- return false;
-
- // build the dylib, only if the order file is defined
- if ( options.swiftGenericMetadataFile.empty() )
- return false;
-
- // check if the metadata builder is available
-#if !BUILDING_CACHE_BUILDER_UNIT_TESTS && !BUILDING_SIM_CACHE_BUILDER
- if ( swift_externalMetadataBuilder_create == nullptr )
- return false;
-#endif // !BUILDING_CACHE_BUILDER_UNIT_TESTS
-
- return true;
-}
-
-Error SharedCacheBuilder::buildSwiftPrespecializedDylibJSON()
-{
-#if !BUILDING_CACHE_BUILDER_UNIT_TESTS && !BUILDING_SIM_CACHE_BUILDER
- Timer::Scope timedScope(this->config, "buildSwiftPrespecializedDylibJSON time");
-
- SwiftExternalMetadataBuilder* builder = swift_externalMetadataBuilder_create((int)options.platform, options.archs.name());
- if ( !builder )
- return Error("swift_externalMetadataBuilder_create failed");
-
- for ( const CacheDylib& dylib : this->cacheDylibs ) {
- if ( dylib.inputMF == nullptr ) continue;
-
- // TODO: rdar://132262275 (dyld shared cache builder should tell Swift Metadata builder also about dyld)
- if ( dylib.inputMF->isDyld() ) continue;
-
- if ( const char* err = swift_externalMetadataBuilder_addDylib(builder, dylib.inputMF->installName(),
- (const struct mach_header*)dylib.inputMF, dylib.inputFile->size) )
- return Error("swift_externalMetadataBuilder_addDylib failed: %s", err);
- }
-
- if ( const char* err = swift_externalMetadataBuilder_readNamesJSON(builder, options.swiftGenericMetadataFile.c_str()) )
- return Error("swift_externalMetadataBuilder_readNamesJSON failed: %s", err);
-
- if ( const char* err = swift_externalMetadataBuilder_buildMetadata(builder) )
- return Error("swift_externalMetadataBuilder_buildMetadata failed: %s", err);
-
- if ( const char* json = swift_externalMetadataBuilder_getMetadataJSON(builder) )
- swiftPrespecializedDylibJSON = json;
- else
- return Error("swift_externalMetadataBuilder_getMetadataJSON returned an empty JSON");
-
- const std::string_view placeholderVersion = R"("platformVersion": "1.0")";
- // Patch platformVersion if it's 1.0 until rdar://122585868 is fixed
- if ( auto pos = swiftPrespecializedDylibJSON.find(placeholderVersion);
- pos != swiftPrespecializedDylibJSON.npos ) {
-
- __block mach_o::Version32 newMinOS;
- // determine new deployment target based on dyld's version
- for ( const InputFile& inputFile : allInputFiles ) {
- if ( !inputFile.mf )
- continue;
-
- if ( !endsWith(inputFile.path, "dyld") )
- continue;
-
- inputFile.mf->forEachSupportedPlatform(^(dyld3::Platform platform, uint32_t currentMinOS, uint32_t sdk) {
- if ( platform == options.platform )
- newMinOS = mach_o::Version32(currentMinOS);
- });
- break;
- }
-
- if ( newMinOS > mach_o::Version32(1, 0) ) {
- char verStr[32];
- newMinOS.toString(verStr);
- std::string newVersion = "\"platformVersion\": \"";
- newVersion += verStr;
- newVersion += "\"";
-
- swiftPrespecializedDylibJSON.replace(pos, placeholderVersion.size(), newVersion);
- }
- }
-
- swift_externalMetadataBuilder_destroy(builder);
-
- if ( options.debug ) {
- std::string path;
- if ( const char* dir = getenv("TMPDIR") )
- path = dir;
- if ( path.empty() )
- path = "/tmp";
- path += "/swift-prespecialized.json-XXXXXX";
-
- int outFileFd = mkstemp(path.data());
- if ( outFileFd != -1 ) {
- write(outFileFd, swiftPrespecializedDylibJSON.data(), swiftPrespecializedDylibJSON.size());
- }
- }
-#endif // !BUILDING_CACHE_BUILDER_UNIT_TESTS
-
- return Error::none();
-}
-
-bool SharedCacheBuilder::reserveSwiftPrespecializedInputFile()
-{
- if ( !shouldBuildSwiftPrespecializedDylib() )
- return false;
-
- InputFile inputFile;
- inputFile.mf = nullptr;
- inputFile.inode = 0;
- inputFile.mtime = 0;
- inputFile.path = swiftPrespecializedDylibInstallName;
- allInputFiles.push_back(std::move(inputFile));
- cacheDylibs.push_back(CacheDylib(swiftPrespecializedDylibInstallName));
- return true;
-}
-
-Error SharedCacheBuilder::createSwiftPrespecializedDylib()
-{
- if ( !shouldBuildSwiftPrespecializedDylib() )
- return Error::none();
-
- if ( Error err = buildSwiftPrespecializedDylibJSON() )
- return err;
-
- InputFile* inputFile = nullptr;
- if ( allInputFiles.empty() || allInputFiles.back().path != swiftPrespecializedDylibInstallName )
- return Error("missing input file placeholder for Swift prespecialized dylib");
- inputFile = &allInputFiles.back();
-
- std::vector<const char*> dylibsList;
- // the dylib list needs to be in order of objc dylibs
- for ( const CacheDylib* dylib : this->objcOptimizer.objcDylibs )
- dylibsList.push_back(CString::dup(dylib->installName).c_str());
-
- // TODO: support in-memory file buffer
- std::string path;
- if ( const char* dir = getenv("TMPDIR") )
- path = dir;
- if ( path.empty() )
- path = "/tmp";
- path += "/libswiftPrespecialized.dylib-XXXXXX";
-
- int outFileFd = mkstemp(path.data());
- if ( outFileFd == -1 )
- return Error("couldn't create a temporary file for Swift prespecialized dylib: %s", (const char*)strerror(errno));
-
- close(outFileFd);
- if ( const char* err = ldMakeDylibFromJSON(swiftPrespecializedDylibJSON, dylibsList, path.c_str()) )
- return Error("%s", err);
-
- // cleanup dylibs list
- for ( const char* str : dylibsList )
- free((void*)str);
-
- // re-open output file
- outFileFd = open(path.c_str(), O_RDONLY);
- if ( outFileFd < 0 )
- return Error("could not open swift dylib file because: %s", (const char*)strerror(errno));
-
- struct stat stat_buf;
- if ( fstat(outFileFd, &stat_buf) == -1 )
- return Error("could not stat swift dylib file because: %s", (const char*)strerror(errno));
-
- vm_size_t bufferSize = stat_buf.st_size;
- void* buffer = mmap(nullptr, bufferSize, PROT_READ, MAP_FILE | MAP_SHARED, outFileFd, 0);
- if ( buffer == MAP_FAILED ) {
- // Failed to mmap the file
- return Error("could not mmap swift dylib file because: %s", (const char*)strerror(errno));
- }
-
- Diagnostics diag;
- inputFile->mf = MachOFile::compatibleSlice(diag, inputFile->size, buffer, bufferSize, path.data(),
- this->options.platform, /* isOSBinary */ false,
- this->options.archs);
- if ( diag.hasError() )
- return Error("%s", diag.errorMessageCStr());
-
- // recreate cache dylib at the reserved slot
- auto cacheDylibIt = std::find_if(cacheDylibs.begin(), cacheDylibs.end(), [](CacheDylib& dylib) {
- return dylib.inputMF == nullptr && dylib.installName == swiftPrespecializedDylibInstallName;
- });
- if ( cacheDylibIt == cacheDylibs.end() )
- return Error("missing cache dylib slot for Swift prespecialized dylib");
-
- // save previously computed cache index
- uint32_t index = cacheDylibIt->cacheIndex;
- // recreate cache dylib with the updated input file
- *cacheDylibIt = CacheDylib(*inputFile);
- cacheDylibIt->cacheIndex = index;
- // rdar://122906481 (Shared cache builder - explicitly model dylibs without a need for a patch table)
- cacheDylibIt->needsPatchTable = false;
- this->swiftPrespecializedDylib = &*cacheDylibIt;
-
- // sanity check Swift dylib compatibility
- __block Error err = Error::none();
- inputFile->mf->withFileLayout(diag, ^(const mach_o::Layout& layout) {
- mach_o::SplitSeg splitSeg(layout);
-
- if ( !splitSeg.isV2() )
- err = Error("Swift prespecialized dylib must use split seg V2");
- });
- if ( !inputFile->mf->hasChainedFixups() )
- err = Error("Swift prespecialized dylib must use chained fixups");
-
- return std::move(err);
-}
-
-// This is phase 4 of the build() process. It takes the inputs and Optimizers
-// from the previous phases, and emits them to the cache buffers
+// This is phase 4 of the build() process. It takes the inputs and Optimizers
+// from the previous phases, and creates the SubCache objects
// Inputs: subCaches, various Optimizers
// Outputs: emitted objc strings in the subCache buffers
Error SharedCacheBuilder::preDylibEmitChunks()
@@ -502,7 +250,6 @@
this->setupSplitSegAdjustors();
this->adjustObjCClasses();
this->adjustObjCProtocols();
- this->adjustObjCCategories();
// Note this could be after dylib passes, but having the strings emitted now makes
// it easier to debug the ObjC dylib passes
@@ -514,8 +261,8 @@
return Error();
}
-// This is phase 5 of the build() process.
-// It runs the passes on each of the cache Dylibs
+// This is phase 4 of the build() process.
+// It runs the passes on each of the cacheDylib's
// Inputs: subCaches, various Optimizers
// Outputs: emitted objc strings in the subCache buffers
Error SharedCacheBuilder::runDylibPasses()
@@ -535,11 +282,6 @@
cacheDylib.copyRawSegments(this->config, aggregateTimer);
- // patch linked dylibs (load commands) as soon as the raw segments were coppied
- // so next steps have accurate view of the dylib
- if ( Error patchErr = this->patchLinkedDylibs(cacheDylib) )
- return patchErr;
-
PatchInfo& dylibPatchInfo = this->patchTableOptimizer.patchInfos[cacheDylib.cacheIndex];
cacheDylib.applySplitSegInfo(diag, this->options, this->config,
aggregateTimer, this->unmappedSymbolsOptimizer);
@@ -550,12 +292,8 @@
if ( diag.hasError() )
return Error("%s", diag.errorMessageCStr());
- std::vector<Error> symbolErrors = cacheDylib.calculateBindTargets(diag, this->config, aggregateTimer, builderCacheDylibs,
- dylibPatchInfo);
- if ( !symbolErrors.empty() ) {
- for ( const Error& symbolErr : symbolErrors )
- this->errors.push_back(symbolErr.message());
- }
+ cacheDylib.calculateBindTargets(diag, this->config, aggregateTimer, builderCacheDylibs,
+ dylibPatchInfo);
if ( diag.hasError() )
return Error("%s", diag.errorMessageCStr());
@@ -596,13 +334,12 @@
return err;
}
-// This is phase 6 of the build() process. It takes the Optimizers
+// This is phase 5 of the build() process. It takes the Optimizers
// from the previous phases, and emits them to the cache buffers
// Inputs: subCaches, various Optimizers
-// Outputs: emitted optimizations in the subCache buffers
+// Outputs: emitted optimiations in the subCache buffers
Error SharedCacheBuilder::postDylibEmitChunks()
{
-
this->optimizeTLVs();
if ( Error error = this->emitUniquedGOTs(); error.hasError() )
@@ -612,25 +349,7 @@
if ( Error error = this->emitCanonicalObjCProtocols(); error.hasError() )
return error;
- this->emitCacheDylibsTrie();
- if ( Error error = this->emitPatchTable(); error.hasError() )
- return error;
-
- // Note, this must be after we emit the patch table
- if ( Error error = this->emitCacheDylibsPrebuiltLoaders(); error.hasError() )
- return error;
-
this->emitObjCHashTables();
-
- bool preAttachedCategories = true;
- if ( preAttachedCategories ) {
- // Note this has to be after anyone walking the objc metadata format
- if ( Error error = this->emitPreAttachedObjCCategories(); error.hasError() )
- return error;
- }
-
- // Note, this must be after emitCacheDylibsPrebuiltLoaders() as it needs the offset to the SectionLocations*
- // in the PrebuiltLoader*
this->emitObjCHeaderInfo();
if ( Error error = this->computeObjCClassLayout(); error.hasError() )
return error;
@@ -644,6 +363,14 @@
if ( Error error = this->emitSwiftHashTables(); error.hasError() )
return error;
+ this->emitCacheDylibsTrie();
+ if ( Error error = this->emitPatchTable(); error.hasError() )
+ return error;
+
+ // Note, this must be after we emit the patch table
+ if ( Error error = this->emitCacheDylibsPrebuiltLoaders(); error.hasError() )
+ return error;
+
// Note, this has to be after we've emitted the objc hash tables and the objc header infos
if ( Error error = this->emitExecutablePrebuiltLoaders(); error.hasError() )
return error;
@@ -658,8 +385,8 @@
return Error();
}
-// This is phase 7 of the build() process. It does any final work
-// to emit the sub caches
+// This is phase 6 of the build() process. it does any final work to emit
+// the sub caches
// Inputs: everything else
// Outputs: final emitted data in the sub caches
Error SharedCacheBuilder::finalize()
@@ -747,17 +474,9 @@
Timer::Scope timedScope(this->config, "categorizeInputs time");
for ( InputFile& inputFile : this->allInputFiles ) {
- if ( inputFile.mf == nullptr ) continue;
-
if ( inputFile.mf->isDylib() || inputFile.mf->isDyld() ) {
- auto failureHandler = ^(const char* format, ...) __attribute__((format(printf, 1, 2))) {
- char* output_string;
- va_list list;
- va_start(list, format);
- vasprintf(&output_string, format, list);
- va_end(list);
- inputFile.setError(Error("%s", (const char*)output_string));
- free(output_string);
+ auto failureHandler = ^(const char* reason) {
+ inputFile.setError(Error("%s", reason));
};
std::string_view installName = inputFile.mf->installName();
@@ -782,7 +501,7 @@
}
}
- if ( !inputFile.forceNotCacheEligible && inputFile.mf->canBePlacedInDyldCache(dylibPath.data(), failureHandler) ) {
+ if ( inputFile.mf->canBePlacedInDyldCache(dylibPath.data(), failureHandler) ) {
CacheDylib cacheDylib(inputFile);
this->cacheDylibs.push_back(std::move(cacheDylib));
}
@@ -818,8 +537,6 @@
__block std::unordered_set<std::string_view> allDylibs;
allDylibs.reserve(this->allInputFiles.size());
for ( const InputFile& inputFile : this->allInputFiles ) {
- if ( inputFile.mf == nullptr ) continue;
-
if ( inputFile.mf->isDylib() )
allDylibs.insert(inputFile.mf->installName());
}
@@ -838,8 +555,6 @@
doAgain = false;
// scan dylib list making sure all dependents are in dylib list
for ( const CacheDylib& cacheDylib : this->cacheDylibs ) {
- if ( cacheDylib.inputFile == nullptr ) continue;
-
//Timer::Scope timedScope(this->config, cacheDylib.installName);
// Skip dylibs we marked bad from a previous iteration
if ( cacheDylib.inputFile->hasError() )
@@ -888,8 +603,6 @@
// Add bad dylibs to the "other" dylibs for use in prebuilt loaders
for ( const CacheDylib& cacheDylib : this->cacheDylibs ) {
- if ( cacheDylib.inputFile == nullptr ) continue;
-
if ( cacheDylib.inputFile->hasError() ) {
this->nonCacheDylibInputFiles.push_back(cacheDylib.inputFile);
this->dylibHasMissingDependency = true;
@@ -898,18 +611,9 @@
this->cacheDylibs.erase(std::remove_if(this->cacheDylibs.begin(), this->cacheDylibs.end(), [&](const CacheDylib& dylib) {
// Dylibs with errors must be removed from the cache
- return dylib.inputFile != nullptr && dylib.inputFile->hasError();
+ return dylib.inputFile->hasError();
}),
this->cacheDylibs.end());
-
- // verify that there's at least one dylib that has an input file
- if ( !std::any_of(cacheDylibs.begin(), cacheDylibs.end(), [](const CacheDylib& dylib) {
- return dylib.inputFile != nullptr;
- }) ) {
- // the only remaining dylib is the synthesized Swift prespecialized dylib
- // so remove it too
- cacheDylibs.clear();
- }
}
void SharedCacheBuilder::calculateDylibAliases()
@@ -1024,30 +728,6 @@
stop = true;
}
});
-
- // copy the original list of dependents
- cacheDylib.inputDependents = cacheDylib.dependents;
-
- // note: below changes to dependents need to be kept in sync with load command patching in `patchLinkedDylibs`
- // we might want to generalize that if more libraries require patching
-
- // force swiftCore link the prespecialized dylib
- if ( swiftPrespecializedDylib && cacheDylib.installName.find("libswiftCore.dylib") != std::string_view::npos ) {
- CacheDylib::DependentDylib depDylib;
- depDylib.kind = CacheDylib::DependentDylib::Kind::normal;
- depDylib.dylib = swiftPrespecializedDylib;
- cacheDylib.dependents.push_back(std::move(depDylib));
- }
-
- // clear all dependents of the prespecialized dylib except libSystem
- // otherwise loading the library would pull in lots of other dependencies
- if ( swiftPrespecializedDylib && &cacheDylib == swiftPrespecializedDylib ) {
- if ( cacheDylib.dependents.empty() || cacheDylib.dependents.front().dylib->installName.find("libSystem") == std::string_view::npos ) {
- diag.error("expected libSystem as the first linked dylib of %s", cacheDylib.inputMF->installName());
- } else {
- cacheDylib.dependents.erase(cacheDylib.dependents.begin()+1, cacheDylib.dependents.end());
- }
- }
if ( diag.hasError() )
return Error("%s", diag.errorMessageCStr());
@@ -1253,18 +933,14 @@
void SharedCacheBuilder::estimateIMPCaches()
{
- // Only LP64 is supported by the runtime
if ( !this->config.layout.is64 )
return;
- // Limited by ImpCacheEntry_v2::impOffset which is 38-bits. For now limit to 16GB
- // as that is the maximum we know slide info v5 can get to
- if ( this->config.layout.cacheSize.rawValue() > 16_GB )
+ if ( this->config.layout.cacheSize.rawValue() > 0x100000000 )
return;
- // Only arm64* are is supported by the runtime
- std::string_view archName = this->options.archs.name();
- if ( archName != "arm64e" && archName != "arm64")
+ // Only iOS for now
+ if ( this->options.platform != dyld3::Platform::iOS )
return;
// Skip everything if the JSON file is empty
@@ -1286,27 +962,6 @@
// 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
@@ -1352,9 +1007,6 @@
});
objcVisitor.forEachCategory(^(const objc_visitor::Category& objcCategory, bool& stopCategory) {
- if ( objcCategory.isForSwiftStubClass() )
- return;
-
imp_caches::Category impCacheCategory(objcCategory.getName(objcVisitor));
// instance methods
@@ -1533,9 +1185,6 @@
// Walk each category and set the class pointer
__block uint32_t categoryIndex = 0;
objcVisitor.forEachCategory(^(const objc_visitor::Category& objcCategory, bool& stopCategory) {
- if ( objcCategory.isForSwiftStubClass() )
- return;
-
imp_caches::Category& impCacheCategory = dylib.categories[categoryIndex];
const DylibClasses& classMap = dylibClassMaps[cacheDylib.cacheIndex];
@@ -1702,8 +1351,6 @@
assert(this->objcOptimizer.objcDylibs.empty());
for ( CacheDylib& cacheDylib : this->cacheDylibs ) {
- if ( cacheDylib.inputMF == nullptr ) continue;
-
if ( cacheDylib.inputMF->hasObjC() )
this->objcOptimizer.objcDylibs.push_back(&cacheDylib);
}
@@ -1724,8 +1371,6 @@
this->objcOptimizer.headerInfoReadWriteByteSize = sizeof(ObjCOptimizer::header_info_rw_list_t);
this->objcOptimizer.headerInfoReadWriteByteSize += (uint32_t)this->objcOptimizer.objcDylibs.size() * sizeof(ObjCOptimizer::header_info_rw_32_t);
}
-
- this->objcOptimizer.imageInfoSize = this->objcOptimizer.objcDylibs.size() * sizeof(objc::objc_image_info);
if ( this->config.log.printStats ) {
stats.add(" objc: found %lld objc dylibs\n", (uint64_t)this->objcOptimizer.objcDylibs.size());
@@ -2106,166 +1751,6 @@
}
}
-// Walk all the dylibs and build a map of ObjC categories
-void SharedCacheBuilder::findObjCCategories()
-{
- if ( this->objcOptimizer.objcDylibs.empty() )
- return;
-
- Stats stats(this->config);
- Timer::Scope timedScope(this->config, "findObjCCategories time");
-
- // Reserve space for 15k categories, as we have 10k as of writing
- const uint32_t numCategoriesToReserve = 1 << 14;
- this->objcCategoryOptimizer.categories.reserve(numCategoriesToReserve);
- size_t objcIndex = 0;
- for (size_t cacheIndex = 0; cacheIndex < this->cacheDylibs.size(); cacheIndex++) {
- CacheDylib& cacheDylib = this->cacheDylibs[cacheIndex];
- if ( !cacheDylib.inputMF->hasObjC() )
- continue;
-
- // Skip dylibs with opcode fixups, as the Category visitor operates on chained fixups to find classes
- if ( cacheDylib.inputMF->hasOpcodeFixups() ) {
- this->objcCategoryOptimizer.excludedDylibs.insert(objcIndex);
- objcIndex++;
- continue;
- }
- struct BindTarget {
- std::string_view symbolName;
- std::optional<uint32_t> targetDylibIndex;
- bool isWeakImport = false;
- };
- __block std::vector<BindTarget> bindTargets;
- __block Diagnostics diag;
- cacheDylib.inputMF->withFileLayout(diag, ^(const mach_o::Layout& layout) {
- mach_o::Fixups fixups(layout);
-
- fixups.forEachBindTarget(diag, false, 0, ^(const mach_o::Fixups::BindTargetInfo& info, bool& stop) {
- if ( info.libOrdinal == BIND_SPECIAL_DYLIB_SELF ) {
- bindTargets.push_back({ info.symbolName, cacheDylib.cacheIndex, info.weakImport });
- } else if ( info.libOrdinal < 0 ) {
- // A special ordinal such as weak. Just put in a placeholder for now
- bindTargets.push_back({ info.symbolName, std::nullopt, info.weakImport });
- } else {
- assert(info.libOrdinal <= (int)cacheDylib.dependents.size());
- const CacheDylib *targetDylib = cacheDylib.dependents[info.libOrdinal-1].dylib;
- assert(info.weakImport || (targetDylib != nullptr));
- std::optional<uint32_t> targetDylibIndex;
- if ( targetDylib != nullptr )
- targetDylibIndex = targetDylib->cacheIndex;
- bindTargets.push_back({ info.symbolName, targetDylibIndex, info.weakImport });
- }
-
- if ( diag.hasError() )
- stop = true;
- }, ^(const mach_o::Fixups::BindTargetInfo& info, bool& stop) {
- // This shouldn't happen with chained fixups
- assert(0);
- });
- });
- diag.assertNoError();
-
- __block objc_visitor::Visitor objCVisitor = makeInputDylibObjCVisitor(cacheDylib);
-
- __block bool categoriesHaveClassProperties = false;
- objCVisitor.withImageInfo(^(const uint32_t version, const uint32_t flags) {
- const uint64_t hasCategoryClassPropertiesFlag = (1 << 6);
- categoriesHaveClassProperties = (flags & hasCategoryClassPropertiesFlag);
- });
-
- objCVisitor.forEachCategory(^(const objc_visitor::Category &objcCategory, bool &stopCategory) {
-
- // Skip catlist2 entries. These are only for Swift stub classes
- if ( objcCategory.isForSwiftStubClass() )
- return;
-
- __block ObjCCategoryOptimizer::Category objCCategoryInfo(objcCategory.getName(objCVisitor));
- objCCategoryInfo.dylibObjcIndex = objcIndex;
- objCCategoryInfo.vmAddress = objcCategory.getVMAddress();
-
- objcCategory.withClass(objCVisitor, ^(const dyld3::MachOFile::ChainedFixupPointerOnDisk *fixup, uint16_t pointerFormat) {
- assert(fixup->raw64 != 0);
-
- uint64_t runtimeOffset = 0;
- if ( fixup->isRebase(pointerFormat, objCVisitor.getOnDiskDylibChainedPointerBaseAddress().rawValue(), runtimeOffset) ) {
- // Rebase to a class in this image.
- objCCategoryInfo.classDylibIndex = cacheDylib.cacheIndex;
- objCCategoryInfo.classVMAddress = VMAddress(runtimeOffset);
- } else {
- uint32_t bindOrdinal = 0;
- int64_t bindAddend = 0;
- if ( fixup->isBind(pointerFormat, bindOrdinal, bindAddend) ) {
- BindTarget& bindTarget = bindTargets[bindOrdinal];
- FoundSymbol foundSymbol = findTargetClass(diag, this->cacheDylibs,
- bindTarget.symbolName,
- bindTarget.targetDylibIndex);
- if ( foundSymbol.foundInDylib == nullptr ) {
- // Ignore category if class is missing. Usually due to a weak-link
- if ( !bindTarget.isWeakImport ) {
- this->warning("Class %s could not be found for category %s in %s.", bindTarget.symbolName.data(), objCCategoryInfo.name.data(), cacheDylib.installName.data());
- }
- return;
- }
- objCCategoryInfo.classVMAddress = VMAddress(foundSymbol.offsetInDylib.rawValue());
- objCCategoryInfo.classDylibIndex = foundSymbol.foundInDylib->cacheIndex;
- } else {
- assert(0);
- }
- }
- });
-
- if ( !objCCategoryInfo.classVMAddress.has_value() )
- return;
-
- // instance methods
- {
- objc_visitor::MethodList objcMethodList = objcCategory.getInstanceMethods(objCVisitor);
- uint32_t numMethods = objcMethodList.numMethods();
- if ( numMethods > 0 )
- objCCategoryInfo.iMethodListVMAddress = objcMethodList.getVMAddress().value();
- }
-
- // class methods
- {
- objc_visitor::MethodList objcMethodList = objcCategory.getClassMethods(objCVisitor);
- uint32_t numMethods = objcMethodList.numMethods();
- if ( numMethods > 0 )
- objCCategoryInfo.cMethodListVMAddress = objcMethodList.getVMAddress().value();
- }
-
- // protocols
- {
- objc_visitor::ProtocolList protocols = objcCategory.getProtocols(objCVisitor);
- uint64_t numProtocols = protocols.numProtocols(objCVisitor);
- if ( numProtocols > 0 )
- objCCategoryInfo.protocolListVMAddress = protocols.getVMAddress().value();
- }
-
- // instance properties
- {
- objc_visitor::PropertyList objcPropertyList = objcCategory.getInstanceProperties(objCVisitor);
- uint64_t numProperties = objcPropertyList.numProperties();
- if ( numProperties > 0 )
- objCCategoryInfo.iPropertyListVMAddress = objcPropertyList.getVMAddress().value();
- }
-
- // class properties
- if ( categoriesHaveClassProperties ) {
- objc_visitor::PropertyList objcPropertyList = objcCategory.getClassProperties(objCVisitor);
- uint64_t numProperties = objcPropertyList.numProperties();
- if ( numProperties > 0 )
- objCCategoryInfo.cPropertyListVMAddress = objcPropertyList.getVMAddress().value();
- }
- this->objcCategoryOptimizer.categories.push_back(std::move(objCCategoryInfo));
- });
- objcIndex++;
- }
-
- if ( this->config.log.printStats ) {
- stats.add(" objc: found %lld categories\n", (uint64_t)this->objcCategoryOptimizer.categories.size());
- }
-}
-
static uint32_t hashTableSize(uint32_t maxElements, uint32_t perElementData)
{
uint32_t elementsWithPadding = maxElements * 11 / 10; // if close to power of 2, perfect hash may fail, so don't get within 10% of that
@@ -2325,71 +1810,6 @@
}
}
-void SharedCacheBuilder::calculateObjCCategoriesSize()
-{
- if ( this->objcOptimizer.objcDylibs.empty() )
- return;
-
- Stats stats(this->config);
- Timer::Scope timedScope(this->config, "calculateObjCCanonicalCategoriesSize time");
-
- uint64_t sizeAndCountSize = sizeof(struct ListOfListsEntry);
- uint64_t listEntrySize = sizeof(struct ListOfListsEntry);
-
- // Add an empty method list that all lists of lists can use if the need it
- // This is used for classes when they don't have method lists, but we want them to
- // FIXME: Get the size from somewhere. Its really the { uint32_t entsize; uint32_t count }
- this->objcCategoryOptimizer.categoriesTotalByteSize += 8;
-
- // This will allocate one listEntrySize for each category list.
- // We might end up using less if the category does not extend a specific list.
- // This will also allocate one listEntrySize and one sizeAndCountSize
- // for every list in the original class.
- // We might end up using less due to multiple categories extending the same class.
- for ( auto& categoryInfo : this->objcCategoryOptimizer.categories ) {
-
- if ( categoryInfo.iMethodListVMAddress.has_value()) {
- // instance methods
- this->objcCategoryOptimizer.categoriesTotalByteSize += sizeAndCountSize;
- this->objcCategoryOptimizer.categoriesTotalByteSize += listEntrySize;
- // original instance methods
- this->objcCategoryOptimizer.categoriesTotalByteSize += listEntrySize;
- }
- if ( categoryInfo.cMethodListVMAddress.has_value()) {
- // class methods
- this->objcCategoryOptimizer.categoriesTotalByteSize += sizeAndCountSize;
- this->objcCategoryOptimizer.categoriesTotalByteSize += listEntrySize;
- // original class methods
- this->objcCategoryOptimizer.categoriesTotalByteSize += listEntrySize;
- }
- if ( categoryInfo.protocolListVMAddress.has_value()) {
- // protocols
- this->objcCategoryOptimizer.categoriesTotalByteSize += sizeAndCountSize;
- this->objcCategoryOptimizer.categoriesTotalByteSize += listEntrySize;
- // original protocols
- this->objcCategoryOptimizer.categoriesTotalByteSize += listEntrySize;
- }
- if ( categoryInfo.iPropertyListVMAddress.has_value()) {
- // instance properties
- this->objcCategoryOptimizer.categoriesTotalByteSize += sizeAndCountSize;
- this->objcCategoryOptimizer.categoriesTotalByteSize += listEntrySize;
- // original instance properties
- this->objcCategoryOptimizer.categoriesTotalByteSize += listEntrySize;
- }
- if ( categoryInfo.cPropertyListVMAddress.has_value()) {
- // instance properties
- this->objcCategoryOptimizer.categoriesTotalByteSize += sizeAndCountSize;
- this->objcCategoryOptimizer.categoriesTotalByteSize += listEntrySize;
- // original class properties
- this->objcCategoryOptimizer.categoriesTotalByteSize += listEntrySize;
- }
- }
-
- if ( this->config.log.printStats ) {
- stats.add(" objc: categories size: %lld\n", (uint64_t)this->objcCategoryOptimizer.categoriesTotalByteSize);
- }
-}
-
// Each conformance entry is 3 uint64_t's internally, plus the space for the hash table
static uint32_t swiftHashTableSize(uint32_t maxElements)
{
@@ -2406,11 +1826,6 @@
// Add in the 3 uint64_t's for the payload
return hashTableSize + (3 * sizeof(uint64_t) * maxElements);
-}
-
-static uint32_t ptrHashTableSize(uint32_t maxElement, uint32_t numPointerKeys)
-{
- return swiftHashTableSize(maxElement) + numPointerKeys * sizeof(uint64_t);
}
void SharedCacheBuilder::estimateSwiftHashTableSizes()
@@ -2468,28 +1883,6 @@
}
auto& optimizer = this->swiftProtocolConformanceOptimizer;
-
- if ( swiftPrespecializedDylib ) {
- Diagnostics diagVal;
- Diagnostics& diag = diagVal;
- SwiftVisitor swiftVisitorVal = makeInputDylibSwiftVisitor(*swiftPrespecializedDylib);
- SwiftVisitor& swiftVisitor = swiftVisitorVal;
- swiftVisitor.forEachPointerHashTable(diag, ^(metadata_visitor::ResolvedValue sectionBase, size_t tableIndex, uint8_t *tableStart, size_t numEntries) {
- assert(optimizer.prespecializedMetadataHashTables.size() == tableIndex);
-
- PointerHashTableOptimizerInfo& tableInfo = optimizer.prespecializedMetadataHashTables.emplace_back();
- swiftVisitor.forEachPointerHashTableRelativeEntry(diag, tableStart, VMAddress(0ull), ^(size_t index, std::span<uint64_t> keys, uint64_t value) {
- assert(!keys.empty() && "pointer keys can't be empty");
-
- ++tableInfo.numEntries;
- tableInfo.numPointerKeys += (uint32_t)keys.size();
- });
-
- tableInfo.size = ptrHashTableSize(tableInfo.numEntries, tableInfo.numPointerKeys);
- assert(tableInfo.numEntries == numEntries);
- });
- }
-
optimizer.typeConformancesHashTableSize = swiftHashTableSize(numTypeConformances);
optimizer.metadataConformancesHashTableSize = swiftHashTableSize(numMetadataConformances);
optimizer.foreignTypeConformancesHashTableSize = swiftHashTableSize(numForeignConformances);
@@ -2499,12 +1892,6 @@
(uint64_t)optimizer.typeConformancesHashTableSize, numTypeConformances);
stats.add(" swift: metadata hash table estimated size: %lld (from %d entries)\n", (uint64_t)optimizer.metadataConformancesHashTableSize, numMetadataConformances);
stats.add(" swift: foreign metadata hash table estimated size: %lld (from %d entries)\n", (uint64_t)optimizer.foreignTypeConformancesHashTableSize, numForeignConformances);
-
- stats.add(" swift: prespecialized metadata hash tables %lu\n", optimizer.prespecializedMetadataHashTables.size());
- for ( int i = 0; i < optimizer.prespecializedMetadataHashTables.size(); ++i ) {
- const PointerHashTableOptimizerInfo& tableInfo = optimizer.prespecializedMetadataHashTables[i];
- stats.add(" swift: prespecialized metadata hash table #%d. estimated size: %lld (from %u entries)\n", i, (uint64_t)tableInfo.size, tableInfo.numEntries);
- }
}
}
@@ -2577,8 +1964,6 @@
__block uint32_t numBinds = 0;
uint32_t numClients = 0;
for ( const CacheDylib& cacheDylib : this->cacheDylibs ) {
- if ( !cacheDylib.needsPatchTable )
- continue;
__block Diagnostics diag;
cacheDylib.inputMF->withFileLayout(diag, ^(const mach_o::Layout& layout) {
mach_o::Fixups fixups(layout);
@@ -2663,7 +2048,7 @@
size += cacheDylib.inputFile->path.size() + 1;
size = alignTo(size, alignof(dyld4::Loader::LoaderRef));
size += sizeof(dyld4::Loader::LoaderRef) * cacheDylib.dependents.size();
- size += sizeof(Loader::LinkedDylibAttributes) * cacheDylib.dependents.size();
+ size += sizeof(Loader::DependentKind) * cacheDylib.dependents.size();
size += sizeof(Loader::FileValidationInfo);
size += sizeof(Loader::Region) * cacheDylib.segments.size();
@@ -2778,7 +2163,16 @@
void SharedCacheBuilder::computeSubCaches()
{
Timer::Scope timedScope(this->config, "computeSubCaches time");
- computeLargeSubCache();
+
+ // We have 3 different kinds of caches.
+ // - regular: put everything in a single file
+ // - large: A file is (TEXT, DATA, LINKEDIT), and we might have > 1 file
+ // - split: A file is TEXT/DATA/LINKEDIT, and we've have 1 or more TEXT, and exactly 1 DATA and LINKEDIT
+ if ( config.layout.large.has_value() ) {
+ computeLargeSubCache();
+ } else {
+ computeRegularSubCache();
+ }
}
// ObjC/Swift optimizations produce arrays, hash tables, string sections, etc.
@@ -2791,33 +2185,27 @@
// Add canonical objc protocols
subCache.addObjCCanonicalProtocolsChunk(this->config, this->objcProtocolOptimizer);
- // Add objc categories
- subCache.addObjCCategoriesChunk(this->config, this->objcCategoryOptimizer);
-
// Add objc opts header
subCache.addObjCOptsHeaderChunk(this->objcOptimizer);
// Add objc header info RO
- subCache.addObjCHeaderInfoReadOnlyChunk(this->config, this->objcOptimizer);
-
- // Add objc image info
- subCache.addObjCImageInfoChunk(this->config, this->objcOptimizer);
+ subCache.addObjCHeaderInfoReadOnlyChunk(this->objcOptimizer);
// Add selector strings and hash table. These need to be adjacent as the table has offsets in
// to the string section
- subCache.addObjCSelectorStringsChunk(this->config, this->objcSelectorOptimizer);
- subCache.addObjCSelectorHashTableChunk(this->config, this->objcSelectorOptimizer);
+ subCache.addObjCSelectorStringsChunk(this->objcSelectorOptimizer);
+ subCache.addObjCSelectorHashTableChunk(this->objcSelectorOptimizer);
// Add class name strings and hash table
- subCache.addObjCClassNameStringsChunk(this->config, this->objcClassOptimizer);
- subCache.addObjCClassHashTableChunk(this->config, this->objcClassOptimizer);
+ subCache.addObjCClassNameStringsChunk(this->objcClassOptimizer);
+ subCache.addObjCClassHashTableChunk(this->objcClassOptimizer);
// Add protocol name strings and hash table
- subCache.addObjCProtocolNameStringsChunk(this->config, this->objcProtocolOptimizer);
- subCache.addObjCProtocolHashTableChunk(this->config, this->objcProtocolOptimizer);
+ subCache.addObjCProtocolNameStringsChunk(this->objcProtocolOptimizer);
+ subCache.addObjCProtocolHashTableChunk(this->objcProtocolOptimizer);
// Add Swift demangled name strings found in ObjC protocol metadata
- subCache.addObjCProtocolSwiftDemangledNamesChunk(this->config, this->objcProtocolOptimizer);
+ subCache.addObjCProtocolSwiftDemangledNamesChunk(this->objcProtocolOptimizer);
// Add ObjC IMP Caches
subCache.addObjCIMPCachesChunk(this->objcIMPCachesOptimizer);
@@ -2829,7 +2217,6 @@
subCache.addSwiftTypeHashTableChunk(this->swiftProtocolConformanceOptimizer);
subCache.addSwiftMetadataHashTableChunk(this->swiftProtocolConformanceOptimizer);
subCache.addSwiftForeignHashTableChunk(this->swiftProtocolConformanceOptimizer);
- subCache.addSwiftPrespecializedMetadataPointerTableChunks(this->swiftProtocolConformanceOptimizer);
}
// The shared cache contains many global optimizations such as dyld4 loaders, trie's, etc.
@@ -2857,7 +2244,7 @@
// anything we need, based on whatever else is already in the SubCache.
void SharedCacheBuilder::addFinalChunksToSubCache(SubCache& subCache)
{
- subCache.addCacheHeaderChunk(this->config, this->cacheDylibs);
+ subCache.addCacheHeaderChunk(this->cacheDylibs);
// Add slide info for each DATA/AUTH segment. Do this after we've added any other DATA*
// segments
@@ -2869,6 +2256,30 @@
// Finalize the SubCache, by removing any unused regions
subCache.removeEmptyRegions();
+}
+
+void SharedCacheBuilder::computeRegularSubCache()
+{
+ // Put everything into a single file.
+ SubCache subCache = SubCache::makeMainCache(this->options, true);
+
+ // Add all the objc tables. This must be done before we add libobjc's __TEXT
+ this->addObjCOptimizationsToSubCache(subCache);
+
+ for ( CacheDylib& cacheDylib : this->cacheDylibs ) {
+ bool addLinkedit = true;
+ subCache.addDylib(cacheDylib, addLinkedit);
+ }
+
+ // Add all the global optimizations
+ this->addGlobalOptimizationsToSubCache(subCache);
+
+ // Reserve space in the last sub cache for dynamic config data
+ subCache.addDynamicConfigChunk();
+
+ this->addFinalChunksToSubCache(subCache);
+
+ this->subCaches.push_back(std::move(subCache));
}
// Add stubs Chunk's for every stubs section in the given text subCache
@@ -3042,16 +2453,13 @@
// Nothing to do here
break;
case cache_builder::Region::Kind::dataConst:
- case cache_builder::Region::Kind::tproConst:
case cache_builder::Region::Kind::data:
case cache_builder::Region::Kind::auth:
- case cache_builder::Region::Kind::authConst:
- case cache_builder::Region::Kind::tproAuthConst:{
+ case cache_builder::Region::Kind::authConst: {
Region& newRegion = newSubCache.regions[(uint32_t)oldRegion.kind];
newRegion.chunks = std::move(oldRegion.chunks);
break;
}
- case cache_builder::Region::Kind::readOnly:
case cache_builder::Region::Kind::linkedit:
case cache_builder::Region::Kind::unmapped:
case cache_builder::Region::Kind::dynamicConfig:
@@ -3073,6 +2481,10 @@
std::list<SubCache>& otherCaches)
{
SubCache* currentSubCache = firstSubCache;
+
+ // We'll add LINKEDIT at the end. As the shared region is <= 4GB in size, we can fit
+ // all the LINKEDIT in the last subCache and still keep it in range of 32-bit offsets
+ bool allLinkeditInLastSubCache = this->config.layout.allLinkeditInLastSubCache;
// Walk all the dylibs, and create a new subCache every time we are about to cross
// the subCacheTextLimit
@@ -3087,7 +2499,7 @@
// If we exceed the current limit, then the current subCache is complete and we need
// to start a new one
- if ( (subCacheTextSize + textSize) > this->config.layout.subCacheTextLimit ) {
+ if ( (subCacheTextSize + textSize) > this->config.layout.large->subCacheTextLimit ) {
// Create a new subCache
otherCaches.push_back(SubCache::makeSubCache(this->options));
currentSubCache = &otherCaches.back();
@@ -3100,16 +2512,23 @@
// The subCache with libobjc gets the header info sections
// Add all the objc tables. This must be done before we add libobjc's __TEXT
- std::string_view libObjcInstallName = "/usr/lib/libobjc.A.dylib";
- if ( dyld3::MachOFile::isExclaveKitPlatform(this->options.platform) )
- libObjcInstallName = "/System/ExclaveKit/usr/lib/libobjc.A.dylib";
- if ( cacheDylib.installName == libObjcInstallName )
+ if ( cacheDylib.installName == "/usr/lib/libobjc.A.dylib" )
this->addObjCOptimizationsToSubCache(*currentSubCache);
- currentSubCache->addDylib(this->config, cacheDylib);
+ // We'll add LINKEDIT at the end. As the shared region is <= 4GB in size, we can fit
+ // all the LINKEDIT in the last subCache and still keep it in range of 32-bit offsets
+ bool addLinkedit = !allLinkeditInLastSubCache;
+ currentSubCache->addDylib(cacheDylib, addLinkedit);
}
// Add all the remaining content in to the final (current) subCache
+
+ // Add linkedit chunks from dylibs, if needed
+ if ( allLinkeditInLastSubCache ) {
+ for ( CacheDylib& cacheDylib : this->cacheDylibs )
+ currentSubCache->addLinkeditFromDylib(cacheDylib);
+ }
+
// Add all the global optimizations
this->addGlobalOptimizationsToSubCache(*currentSubCache);
@@ -3488,13 +2907,6 @@
sourceStringSize += symbolString.size() + 1;
++sourceStringCount;
- // rdar://129398821 (dyld cache builder add support for binds relative to dylib segments)
- // skip synthetic dyld symbols
- if ( symbolString.find("$dyld$") != std::string_view::npos ) {
- ++oldSymbolIndex;
- return;
- }
-
auto itAndInserted = stringMap.insert({ symbolString, stringBufferSize });
// If we inserted the string, then account for the space
if ( itAndInserted.second )
@@ -3951,7 +3363,7 @@
Error SharedCacheBuilder::calculateUniqueGOTs()
{
// Skip this optimiation on simulator until we've qualified it there
- if ( this->options.isSimulator() )
+ if ( this->options.isSimultor() )
return Error();
Stats stats(this->config);
@@ -3983,51 +3395,20 @@
if ( (dataConstRegion == nullptr) && (authConstRegion == nullptr) )
continue;
- for ( UniquedGOTKind sectionKind : { UniquedGOTKind::regular, UniquedGOTKind::authGot, UniquedGOTKind::authPtr } ) {
-
- Region* region = nullptr;
- std::string_view segmentName;
- std::string_view sectionName;
- const char* kindName = nullptr;
- CoalescedGOTSection* subCacheUniquedGOTs = nullptr;
-
- // Skip sections if their segment doesn't exist
- switch ( sectionKind ) {
- case UniquedGOTKind::regular:
- if ( dataConstRegion == nullptr )
- continue;
-
- region = dataConstRegion;
- segmentName = "__DATA_CONST";
- sectionName = "__got";
- kindName = "regular";
- subCacheUniquedGOTs = &subCache.uniquedGOTsOptimizer.regularGOTs;
- break;
- case UniquedGOTKind::authGot:
- if ( authConstRegion == nullptr )
- continue;
-
- region = authConstRegion;
- segmentName = "__AUTH_CONST";
- sectionName = "__auth_got";
- kindName = "auth-gots";
- subCacheUniquedGOTs = &subCache.uniquedGOTsOptimizer.authGOTs;
- break;
- case UniquedGOTKind::authPtr:
- if ( authConstRegion == nullptr )
- continue;
-
- region = authConstRegion;
- segmentName = "__AUTH_CONST";
- sectionName = "__auth_ptr";
- kindName = "auth-ptrs";
- subCacheUniquedGOTs = &subCache.uniquedGOTsOptimizer.authPtrs;
- break;
- }
+ for ( bool auth : { false, true } ) {
+ if ( auth && (authConstRegion == nullptr) )
+ continue;
+ if ( !auth && (dataConstRegion == nullptr) )
+ continue;
+
+ Region& region = auth ? *authConstRegion : *dataConstRegion;
+ std::string_view segmentName = auth ? "__AUTH_CONST" : "__DATA_CONST";
+ std::string_view sectionName = auth ? "__auth_got" : "__got";
+ CoalescedGOTSection& subCacheUniquedGOTs = auth ? subCache.uniquedGOTsOptimizer.authGOTs : subCache.uniquedGOTsOptimizer.regularGOTs;
std::vector<DylibSectionCoalescer::OptimizedSection*> dylibOptimizedSections;
- dylibOptimizedSections.reserve(region->chunks.size());
- for ( const Chunk* chunk : region->chunks ) {
+ dylibOptimizedSections.reserve(region.chunks.size());
+ for ( const Chunk* chunk : region.chunks ) {
const DylibSegmentChunk* segmentChunk = chunk->isDylibSegmentChunk();
if ( !segmentChunk )
continue;
@@ -4035,28 +3416,17 @@
if ( chunk->name() != segmentName )
continue;
- CacheDylib* dylib = fileToDylibMap.at(segmentChunk->inputFile);
- DylibSectionCoalescer::OptimizedSection* dylibUniquedGOTs = nullptr;
- switch ( sectionKind ) {
- case UniquedGOTKind::regular:
- dylibUniquedGOTs = &dylib->optimizedSections.gots;
- break;
- case UniquedGOTKind::authGot:
- dylibUniquedGOTs = &dylib->optimizedSections.auth_gots;
- break;
- case UniquedGOTKind::authPtr:
- dylibUniquedGOTs = &dylib->optimizedSections.auth_ptrs;
- break;
- }
+ CacheDylib* dylib = fileToDylibMap.at(segmentChunk->inputFile);
+ auto& dylibUniquedGOTs = auth ? dylib->optimizedSections.auth_gots : dylib->optimizedSections.gots;
// Set the dylib GOTs to point to the subCache they'll be uniqued to
- dylibUniquedGOTs->subCacheSection = subCacheUniquedGOTs;
- dylibOptimizedSections.push_back(dylibUniquedGOTs);
-
- parseGOTs(dylib, segmentChunk, segmentName, sectionName, *dylibUniquedGOTs);
- }
-
- if ( subCacheUniquedGOTs->gotTargetsToOffsets.empty() )
+ dylibUniquedGOTs.subCacheSection = &subCacheUniquedGOTs;
+ dylibOptimizedSections.push_back(&dylibUniquedGOTs);
+
+ parseGOTs(dylib, segmentChunk, segmentName, sectionName, dylibUniquedGOTs);
+ }
+
+ if ( subCacheUniquedGOTs.gotTargetsToOffsets.empty() )
continue;
// Sort the coalesced GOTs based on the target install name. We find GOTs in the order we parse
@@ -4064,8 +3434,8 @@
// each other
typedef CoalescedGOTSection::GOTKey Key;
std::vector<Key> sortedKeys;
- sortedKeys.reserve(subCacheUniquedGOTs->gotTargetsToOffsets.size());
- for ( const auto& keyAndValue : subCacheUniquedGOTs->gotTargetsToOffsets )
+ sortedKeys.reserve(subCacheUniquedGOTs.gotTargetsToOffsets.size());
+ for ( const auto& keyAndValue : subCacheUniquedGOTs.gotTargetsToOffsets )
sortedKeys.push_back(keyAndValue.first);
std::sort(sortedKeys.begin(), sortedKeys.end(),
@@ -4093,8 +3463,8 @@
std::unordered_map<uint32_t, uint32_t> oldToNewOffsetMap;
for ( uint32_t i = 0; i != sortedKeys.size(); ++i ) {
const Key& key = sortedKeys[i];
- auto it = subCacheUniquedGOTs->gotTargetsToOffsets.find(key);
- assert(it != subCacheUniquedGOTs->gotTargetsToOffsets.end());
+ auto it = subCacheUniquedGOTs.gotTargetsToOffsets.find(key);
+ assert(it != subCacheUniquedGOTs.gotTargetsToOffsets.end());
uint32_t newCacheSectionOffset = i * pointerSize;
@@ -4119,34 +3489,26 @@
}
// Add the new chunks to the subCache
- switch ( sectionKind ) {
- case UniquedGOTKind::regular:
- subCache.uniquedGOTs = std::make_unique<UniquedGOTsChunk>();
- subCache.uniquedGOTs->cacheVMSize = CacheVMSize((uint64_t)subCacheUniquedGOTs->gotTargetsToOffsets.size() * pointerSize);
- subCache.uniquedGOTs->subCacheFileSize = CacheFileSize((uint64_t)subCacheUniquedGOTs->gotTargetsToOffsets.size() * pointerSize);
-
- region->chunks.push_back(subCache.uniquedGOTs.get());
-
- subCache.uniquedGOTsOptimizer.regularGOTs.cacheChunk = subCache.uniquedGOTs.get();
- break;
- case UniquedGOTKind::authGot:
- subCache.uniquedAuthGOTs = std::make_unique<UniquedGOTsChunk>();
- subCache.uniquedAuthGOTs->cacheVMSize = CacheVMSize((uint64_t)subCacheUniquedGOTs->gotTargetsToOffsets.size() * pointerSize);
- subCache.uniquedAuthGOTs->subCacheFileSize = CacheFileSize((uint64_t)subCacheUniquedGOTs->gotTargetsToOffsets.size() * pointerSize);
-
- region->chunks.push_back(subCache.uniquedAuthGOTs.get());
-
- subCache.uniquedGOTsOptimizer.authGOTs.cacheChunk = subCache.uniquedAuthGOTs.get();
- break;
- case UniquedGOTKind::authPtr:
- subCache.uniquedAuthPtrs = std::make_unique<UniquedGOTsChunk>();
- subCache.uniquedAuthPtrs->cacheVMSize = CacheVMSize((uint64_t)subCacheUniquedGOTs->gotTargetsToOffsets.size() * pointerSize);
- subCache.uniquedAuthPtrs->subCacheFileSize = CacheFileSize((uint64_t)subCacheUniquedGOTs->gotTargetsToOffsets.size() * pointerSize);
-
- region->chunks.push_back(subCache.uniquedAuthPtrs.get());
-
- subCache.uniquedGOTsOptimizer.authPtrs.cacheChunk = subCache.uniquedAuthPtrs.get();
- break;
+ if ( auth ) {
+ subCache.uniquedAuthGOTs = std::make_unique<UniquedGOTsChunk>();
+ subCache.uniquedAuthGOTs->cacheVMSize = CacheVMSize((uint64_t)subCacheUniquedGOTs.gotTargetsToOffsets.size() * pointerSize);
+ subCache.uniquedAuthGOTs->subCacheFileSize = CacheFileSize((uint64_t)subCacheUniquedGOTs.gotTargetsToOffsets.size() * pointerSize);
+
+ region.chunks.push_back(subCache.uniquedAuthGOTs.get());
+
+ // FIXME: Do we need this. No-one seems to read it from here, or could get it from the subCache instead
+ subCache.uniquedGOTsOptimizer.authGOTsChunk = subCache.uniquedAuthGOTs.get();
+ subCache.uniquedGOTsOptimizer.authGOTs.cacheChunk = subCache.uniquedGOTsOptimizer.authGOTsChunk;
+ } else {
+ subCache.uniquedGOTs = std::make_unique<UniquedGOTsChunk>();
+ subCache.uniquedGOTs->cacheVMSize = CacheVMSize((uint64_t)subCacheUniquedGOTs.gotTargetsToOffsets.size() * pointerSize);
+ subCache.uniquedGOTs->subCacheFileSize = CacheFileSize((uint64_t)subCacheUniquedGOTs.gotTargetsToOffsets.size() * pointerSize);
+
+ region.chunks.push_back(subCache.uniquedGOTs.get());
+
+ // FIXME: Do we need this. No-one seems to read it from here, or could get it from the subCache instead
+ subCache.uniquedGOTsOptimizer.regularGOTsChunk = subCache.uniquedGOTs.get();
+ subCache.uniquedGOTsOptimizer.regularGOTs.cacheChunk = subCache.uniquedGOTsOptimizer.regularGOTsChunk;
}
if ( this->config.log.printStats ) {
@@ -4154,8 +3516,9 @@
for ( DylibSectionCoalescer::OptimizedSection* dylibOptimizedSection : dylibOptimizedSections ) {
totalSourceGOTs += dylibOptimizedSection->offsetMap.size();
}
+ const char* kind = auth ? "auth" : "regular";
stats.add(" got uniquing: uniqued %lld %s GOTs to %lld GOTs\n",
- totalSourceGOTs, kindName, (uint64_t)subCacheUniquedGOTs->gotTargetsToOffsets.size());
+ totalSourceGOTs, kind, (uint64_t)subCacheUniquedGOTs.gotTargetsToOffsets.size());
}
}
}
@@ -4188,8 +3551,7 @@
const DylibSegmentChunk* segmentA = a->isDylibSegmentChunk();
const DylibSegmentChunk* segmentB = b->isDylibSegmentChunk();
- // There can be data chunks that aren't dylib segments, e.g. ObjCHeaderInfoReadWriteChunk.
- if ( segmentA && segmentB && segmentA->kind == DylibSegmentChunk::Kind::dylibDataDirty ) {
+ if ( segmentA->kind == DylibSegmentChunk::Kind::dylibDataDirty ) {
const auto& orderA = dirtyDataSegmentOrdering.find(segmentA->inputFile->path);
const auto& orderB = dirtyDataSegmentOrdering.find(segmentB->inputFile->path);
bool foundA = (orderA != dirtyDataSegmentOrdering.end());
@@ -4205,25 +3567,13 @@
return false;
}
- const DylibSegmentChunk* dylibA = a->isTPROChunk();
- const DylibSegmentChunk* dylibB = b->isTPROChunk();
- // Note this shouldn't be possible, but best to be safe and avoid asserting
- if ( dylibA && dylibB ) {
- // Sort dyld last so that its allocator gets packed with TPRO from other dylibs
- bool isDyldA = dylibA->inputFile->path == "/usr/lib/dyld";
- bool isDyldB = dylibB->inputFile->path == "/usr/lib/dyld";
- if ( isDyldA != isDyldB )
- return !isDyldA;
- }
-
// Note we are using a stable sort, so if the kind's aren't different, return false
// and we'll keep Section's in the order they were added to the vector
return false;
};
- auto dataConstSortOrder = [](const Chunk* a, const Chunk* b) -> bool {
- // Sort TPRO_CONST before DATA_CONST. This only happens on x86_64
- // where we put TPRO_CONST and DATA_CONST in the same Region
+ auto linkeditSortOrder = [](const Chunk* a, const Chunk* b) -> bool {
+ // Sort read-only segments before LINKEDIT
if ( a->sortOrder() != b->sortOrder() )
return a->sortOrder() < b->sortOrder();
@@ -4232,34 +3582,7 @@
return false;
};
- auto tproConstSortOrder = [](const Chunk* a, const Chunk* b) -> bool {
- const DylibSegmentChunk* dylibA = a->isTPROChunk();
- const DylibSegmentChunk* dylibB = b->isTPROChunk();
- // Note this shouldn't be possible, but best to be safe and avoid asserting
- if ( !dylibA || !dylibB )
- return false;
-
- // Sort dyld last so that its allocator gets packed with TPRO from other dylibs
- bool isDyldA = dylibA->inputFile->path == "/usr/lib/dyld";
- bool isDyldB = dylibB->inputFile->path == "/usr/lib/dyld";
- if ( isDyldA != isDyldB )
- return !isDyldA;
-
- // Note we are using a stable sort, so if the kind's aren't different, return false
- // and we'll keep Section's in the order they were added to the vector
- return false;
- };
-
- auto linkeditSortOrder = [](const Chunk* a, const Chunk* b) -> bool {
- // Sort read-only segments before LINKEDIT
- if ( a->sortOrder() != b->sortOrder() )
- return a->sortOrder() < b->sortOrder();
-
- // Note we are using a stable sort, so if the kind's aren't different, return false
- // and we'll keep Section's in the order they were added to the vector
- return false;
- };
-
+ // Only sort data/auth. Everything else is already in order
for ( SubCache& subCache : this->subCaches ) {
for ( Region& region : subCache.regions ) {
if ( region.kind == Region::Kind::text ) {
@@ -4268,41 +3591,10 @@
else if ( (region.kind == Region::Kind::data) || (region.kind == Region::Kind::auth) ) {
std::stable_sort(region.chunks.begin(), region.chunks.end(), dataSortOrder);
}
- else if ( region.kind == Region::Kind::dataConst ) {
- std::stable_sort(region.chunks.begin(), region.chunks.end(), dataConstSortOrder);
- }
- else if ( (region.kind == Region::Kind::tproConst) || (region.kind == Region::Kind::tproAuthConst) ) {
- std::stable_sort(region.chunks.begin(), region.chunks.end(), tproConstSortOrder);
- }
else if ( region.kind == Region::Kind::linkedit ) {
std::stable_sort(region.chunks.begin(), region.chunks.end(), linkeditSortOrder);
}
}
- }
-
- // After sorting, we have to add alignment chunks before/after x86_64 TPRO
- if ( this->config.layout.tproIsInData )
- addAlignmentChunks();
-}
-
-void SharedCacheBuilder::addAlignmentChunks()
-{
- for ( SubCache& subCache : this->subCaches ) {
- SubCache::forEachTPRORegionInData(&subCache, {}, ^(Region& region, const Chunk *firstChunk, const Chunk *lastChunk) {
- // Add alignment before the first chunk
- {
- auto firstPos = std::find(region.chunks.begin(), region.chunks.end(), firstChunk);
- assert(firstPos != region.chunks.end());
- region.chunks.insert(firstPos, ®ion.alignmentChunks.emplace_back());
- }
-
- // Add alignment after the last chunk
- {
- auto lastPos = std::find(region.chunks.begin(), region.chunks.end(), lastChunk);
- assert(lastPos != region.chunks.end());
- region.chunks.insert(lastPos + 1, ®ion.alignmentChunks.emplace_back());
- }
- });
}
}
@@ -4351,9 +3643,6 @@
case cache_builder::SlideInfo::SlideInfoFormat::v3:
slideInfoSize += sizeof(dyld_cache_slide_info3);
break;
- case cache_builder::SlideInfo::SlideInfoFormat::v5:
- slideInfoSize += sizeof(dyld_cache_slide_info5);
- break;
}
slideInfoSize += pagesToSlide * builderConfig.slideInfo.slideInfoBytesPerDataPage;
@@ -4362,10 +3651,8 @@
};
for ( const SubCache& subCache : this->subCaches ) {
- calculateRegionSlideInfoSize(this->config, Region::Kind::tproConst, subCache.regions, subCache.tproConstSlideInfo);
calculateRegionSlideInfoSize(this->config, Region::Kind::data, subCache.regions, subCache.dataSlideInfo);
calculateRegionSlideInfoSize(this->config, Region::Kind::dataConst, subCache.regions, subCache.dataConstSlideInfo);
- calculateRegionSlideInfoSize(this->config, Region::Kind::tproAuthConst, subCache.regions, subCache.tproAuthConstSlideInfo);
calculateRegionSlideInfoSize(this->config, Region::Kind::auth, subCache.regions, subCache.authSlideInfo);
calculateRegionSlideInfoSize(this->config, Region::Kind::authConst, subCache.regions, subCache.authConstSlideInfo);
}
@@ -4397,7 +3684,7 @@
void SharedCacheBuilder::printSubCaches() const
{
- const bool printSegments = this->config.log.printDebug;
+ const bool printSegments = false;
if ( !this->config.log.printStats )
return;
@@ -4416,20 +3703,11 @@
case Region::Kind::dataConst:
regionName = "dataConst";
break;
- case Region::Kind::tproConst:
- regionName = "tproConst";
- break;
case Region::Kind::auth:
regionName = "auth";
break;
case Region::Kind::authConst:
regionName = "authConst";
- break;
- case Region::Kind::tproAuthConst:
- regionName = "tproAuthConst";
- break;
- case Region::Kind::readOnly:
- regionName = "readOnly";
break;
case Region::Kind::linkedit:
regionName = "linkedit";
@@ -4584,6 +3862,106 @@
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();
+}
+
+// This is the x86_64 sim layout, where each of TEXT/DATA/LINKEDIT has its own fixed address
+Error SharedCacheBuilder::computeSubCacheDiscontiguousSimVMLayout()
+{
+ // Add padding between each region, and set the Region VMAddr's
+ CacheVMAddress maxVMAddress = this->config.layout.cacheBaseAddress;
+ assert(this->subCaches.size() == 1);
+ SubCache& subCache = this->subCaches.front();
+ subCache.subCacheVMAddress = this->config.layout.cacheBaseAddress;
+
+ bool seenText = false;
+ bool seenData = false;
+ bool seenLinkedit = false;
+ bool seenDynamicConfig = false;
+ CacheVMAddress lastDataEnd;
+ CacheVMAddress linkEditEnd;
+ for ( Region& region : subCache.regions ) {
+ switch ( region.kind ) {
+ case Region::Kind::text:
+ assert(!seenText);
+ seenText = true;
+ region.subCacheVMAddress = this->config.layout.discontiguous->simTextBaseAddress;
+
+ // Check for overflow
+ if ( 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());
+ }
+ break;
+ case Region::Kind::dataConst:
+ case Region::Kind::data:
+ case Region::Kind::auth:
+ case Region::Kind::authConst:
+ if ( seenData ) {
+ // This data follows from the previous one
+ region.subCacheVMAddress = lastDataEnd;
+ } else {
+ seenData = true;
+ region.subCacheVMAddress = this->config.layout.discontiguous->simDataBaseAddress;
+ }
+ lastDataEnd = region.subCacheVMAddress + region.subCacheVMSize;
+ break;
+ case Region::Kind::linkedit:
+ assert(!seenLinkedit);
+ seenLinkedit = true;
+ region.subCacheVMAddress = this->config.layout.discontiguous->simLinkeditBaseAddress;
+
+ // Check for overflow
+ if ( region.subCacheVMSize > this->config.layout.discontiguous->simLinkeditSize ) {
+ return Error("Overflow in linkedit (0x%llx > 0x%llx)",
+ region.subCacheVMSize.rawValue(),
+ this->config.layout.discontiguous->simLinkeditSize.rawValue());
+ }
+ linkEditEnd = region.subCacheVMAddress + region.subCacheVMSize;
+ break;
+ case Region::Kind::dynamicConfig:
+ assert(!seenDynamicConfig);
+ seenDynamicConfig = true;
+ // Grab space right after the linkedit
+ region.subCacheVMAddress = linkEditEnd;
+ // Check for overflow
+ if ( region.subCacheVMSize > this->config.layout.discontiguous->simLinkeditSize ) {
+ return Error("Overflow in dynamicConfig (0x%llx > 0x%llx)",
+ region.subCacheVMSize.rawValue(),
+ this->config.layout.discontiguous->simLinkeditSize.rawValue());
+ }
+ break;
+ case Region::Kind::unmapped:
+ case Region::Kind::codeSignature:
+ break;
+ case Region::Kind::numKinds:
+ assert(0);
+ break;
+ }
+
+ if ( seenData ) {
+ // Check for overflow
+ CacheVMSize dataSize(lastDataEnd.rawValue() - this->config.layout.discontiguous->simDataBaseAddress.rawValue());
+ if ( dataSize > this->config.layout.discontiguous->simDataSize ) {
+ return Error("Overflow in data (0x%llx > 0x%llx)",
+ dataSize.rawValue(),
+ this->config.layout.discontiguous->simDataSize.rawValue());
+ }
+ }
+
+ if ( region.needsSharedCacheReserveAddressSpace() )
+ maxVMAddress = region.subCacheVMAddress + region.subCacheVMSize;
+ }
+
+ this->totalVMSize = CacheVMSize((maxVMAddress - this->config.layout.cacheBaseAddress).rawValue());
+
return Error();
}
@@ -4631,15 +4009,12 @@
case Region::Kind::codeSignature:
case Region::Kind::numKinds:
break;
- case Region::Kind::tproConst:
case Region::Kind::data:
case Region::Kind::dataConst:
- case Region::Kind::tproAuthConst:
case Region::Kind::auth:
case Region::Kind::authConst:
lastReadWriteRegion = ®ion;
break;
- case Region::Kind::readOnly:
case Region::Kind::dynamicConfig:
case Region::Kind::linkedit:
lastReadOnlyRegion = ®ion;
@@ -4676,105 +4051,14 @@
}
this->totalVMSize = CacheVMSize((vmAddress - this->config.layout.cacheBaseAddress).rawValue());
+
+ 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();
-}
-
-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] = { };
-
- 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
@@ -4853,15 +4137,13 @@
if ( Error error = computeSubCacheContiguousVMLayout(); error.hasError() )
return error;
} else {
- if ( Error error = computeSubCacheDiscontiguousVMLayout(); error.hasError() )
- 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());
+ if ( this->options.isSimultor() ) {
+ if ( Error error = computeSubCacheDiscontiguousSimVMLayout(); error.hasError() )
+ return error;
+ } else {
+ if ( Error error = computeSubCacheDiscontiguousVMLayout(); error.hasError() )
+ return error;
+ }
}
// Update Section VMAddr's now that we know where all the Region's are in memory
@@ -4878,12 +4160,18 @@
}
}
+ 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();
}
Error SharedCacheBuilder::allocateSubCacheBuffers()
{
- const bool log = this->options.debug;
+ const bool log = false;
Timer::Scope timedScope(this->config, "allocateSubCacheBuffers time");
@@ -5199,76 +4487,6 @@
}
}
-void SharedCacheBuilder::adjustObjCCategories()
-{
- if ( this->objcOptimizer.objcDylibs.empty() )
- return;
-
- Timer::Scope timedScope(this->config, "adjustObjCCategories time");
-
- // Categories were stored as input dylib VMAddr's. Convert to cache dylib VMAddr's
- for ( auto& categoryInfo : this->objcCategoryOptimizer.categories ) {
- CacheDylib* cacheDylib = this->objcOptimizer.objcDylibs[categoryInfo.dylibObjcIndex.value()];
-
- // category address
- if ( categoryInfo.vmAddress.has_value() ) {
- uint64_t inputAddr = categoryInfo.vmAddress.value().rawValue();
- InputDylibVMAddress inputVMAddr(inputAddr);
- CacheVMAddress cacheVMAddr = cacheDylib->adjustor->adjustVMAddr(inputVMAddr);
- categoryInfo.vmAddress = VMAddress(cacheVMAddr.rawValue());
- }
-
- // class address
- if ( categoryInfo.classVMAddress.has_value() ) {
- CacheDylib& classDylib = this->cacheDylibs[categoryInfo.classDylibIndex.value()];
- uint64_t inputAddr = categoryInfo.classVMAddress.value().rawValue();
- InputDylibVMAddress inputVMAddr(inputAddr);
- CacheVMAddress cacheVMAddr = classDylib.adjustor->adjustVMAddr(inputVMAddr);
- categoryInfo.classVMAddress = VMAddress(cacheVMAddr.rawValue());
- }
-
- // instance methods
- if ( categoryInfo.iMethodListVMAddress.has_value() ) {
- uint64_t inputAddr = categoryInfo.iMethodListVMAddress.value().rawValue();
- InputDylibVMAddress inputVMAddr(inputAddr);
- CacheVMAddress cacheVMAddr = cacheDylib->adjustor->adjustVMAddr(inputVMAddr);
- categoryInfo.iMethodListVMAddress = VMAddress(cacheVMAddr.rawValue());
- }
-
- // class methods
- if ( categoryInfo.cMethodListVMAddress.has_value() ) {
- uint64_t inputAddr = categoryInfo.cMethodListVMAddress.value().rawValue();
- InputDylibVMAddress inputVMAddr(inputAddr);
- CacheVMAddress cacheVMAddr = cacheDylib->adjustor->adjustVMAddr(inputVMAddr);
- categoryInfo.cMethodListVMAddress = VMAddress(cacheVMAddr.rawValue());
- }
-
- // protocols
- if ( categoryInfo.protocolListVMAddress.has_value() ) {
- uint64_t inputAddr = categoryInfo.protocolListVMAddress.value().rawValue();
- InputDylibVMAddress inputVMAddr(inputAddr);
- CacheVMAddress cacheVMAddr = cacheDylib->adjustor->adjustVMAddr(inputVMAddr);
- categoryInfo.protocolListVMAddress = VMAddress(cacheVMAddr.rawValue());
- }
-
- // instance properties
- if ( categoryInfo.iPropertyListVMAddress.has_value() ) {
- uint64_t inputAddr = categoryInfo.iPropertyListVMAddress.value().rawValue();
- InputDylibVMAddress inputVMAddr(inputAddr);
- CacheVMAddress cacheVMAddr = cacheDylib->adjustor->adjustVMAddr(inputVMAddr);
- categoryInfo.iPropertyListVMAddress = VMAddress(cacheVMAddr.rawValue());
- }
-
- // class properties
- if ( categoryInfo.cPropertyListVMAddress.has_value() ) {
- uint64_t inputAddr = categoryInfo.cPropertyListVMAddress.value().rawValue();
- InputDylibVMAddress inputVMAddr(inputAddr);
- CacheVMAddress cacheVMAddr = cacheDylib->adjustor->adjustVMAddr(inputVMAddr);
- categoryInfo.cPropertyListVMAddress = VMAddress(cacheVMAddr.rawValue());
- }
- }
-}
-
Error SharedCacheBuilder::emitPatchTable()
{
Stats stats(this->config);
@@ -5277,7 +4495,7 @@
// Skip this optimization on simulator until we've qualified it there
__block PatchTableBuilder::PatchableClassesSet patchableObjCClasses;
__block PatchTableBuilder::PatchableSingletonsSet patchableCFObj2;
- if ( !this->options.isSimulator() ) {
+ if ( !this->options.isSimultor() ) {
for ( const CacheDylib& cacheDylib : this->cacheDylibs ) {
__block objc_visitor::Visitor objcVisitor = makeInputDylibObjCVisitor(cacheDylib);
objcVisitor.forEachClassAndMetaClass(^(const objc_visitor::Class& objcClass, bool& stopClass) {
@@ -5323,21 +4541,13 @@
// dyld4 needs a fake "main.exe" to set up the state.
// On macOS this *has* to come from an actual executable, as choosing a zippered
// dylib may incorrectly lead to setting up the ProcessConfig as iOSMac.
-// Simulators and ExclaveKit don't have executables yet so choose a dylib there
+// Simulators don't have executables yet so choose a dylib there
static const MachOFile* getFakeMainExecutable(const BuilderOptions& options,
std::span<CacheDylib> cacheDylibs,
std::span<InputFile*> executableFiles)
{
- if ( options.isSimulator() ) {
+ if ( options.isSimultor() ) {
std::string_view installName = "/usr/lib/libSystem.B.dylib";
- for ( const CacheDylib& cacheDylib : cacheDylibs ) {
- if ( cacheDylib.installName == installName ) {
- assert(cacheDylib.cacheMF != nullptr);
- return cacheDylib.cacheMF;
- }
- }
- } else if (options.isExclaveKit() ) {
- std::string_view installName = "/System/ExclaveKit/usr/lib/libSystem.dylib";
for ( const CacheDylib& cacheDylib : cacheDylibs ) {
if ( cacheDylib.installName == installName ) {
assert(cacheDylib.cacheMF != nullptr);
@@ -5592,7 +4802,7 @@
Diagnostics loadDiag;
((Loader*)ldr)->loadDependents(loadDiag, state, options);
if ( loadDiag.hasError() ) {
- return Error("%s, loading dependents of %s", loadDiag.errorMessageCStr(), ldr->path(state));
+ return Error("%s, loading dependents of %s", loadDiag.errorMessageCStr(), ldr->path());
}
}
@@ -5624,7 +4834,7 @@
return Error("Could not find a main executable for building cache loaders");
const LayoutBuilder layoutBuilder(cacheDylibs, { });
- STACK_ALLOCATOR(processConfigAlloc, 0);
+ EphemeralAllocator processConfigAlloc;
__block dyld4::Vector<ProcessConfig::DyldCache::CacheDylib> processConfigDylibs(processConfigAlloc);
for ( uint32_t dylibIndex = 0; dylibIndex != this->cacheDylibs.size(); ++dylibIndex ) {
@@ -5647,12 +4857,11 @@
}
// build PrebuiltLoaderSet of all dylibs in cache
- STACK_ALLOCATOR(alloc, 0);
KernelArgs kernArgs(mainExecutable, { "test.exe" }, {}, {});
SyscallDelegate osDelegate;
+ EphemeralAllocator alloc;
ProcessConfig processConfig(&kernArgs, osDelegate, alloc);
- RuntimeLocks locks;
- RuntimeState state(processConfig, locks, alloc);
+ RuntimeState state(processConfig, alloc);
// FIXME: This is terrible and needs to be a real reset method
processConfig.dyldCache.cacheBuilderDylibs = &processConfigDylibs;
@@ -5708,7 +4917,7 @@
VMAddress& protocolClassVMAddr, MachOFile::PointerMetaData& protocolClassPMD)
{
for ( CacheDylib* cacheDylib : objcDylibs ) {
- if ( cacheDylib->installName.ends_with("/usr/lib/libobjc.A.dylib" )) {
+ if ( cacheDylib->installName == "/usr/lib/libobjc.A.dylib" ) {
__block InputDylibVMAddress inputOptPtrsVMAddress;
__block uint64_t sectionSize = 0;
__block bool found = false;
@@ -5742,7 +4951,7 @@
CacheVMAddress cacheOptPtrsVMAddr = cacheDylib->adjustor->adjustVMAddr(inputOptPtrsVMAddress);
- objc_visitor::Visitor objcVisitor = cacheDylib->makeCacheObjCVisitor(config, nullptr, nullptr, nullptr);
+ objc_visitor::Visitor objcVisitor = cacheDylib->makeCacheObjCVisitor(config, nullptr, nullptr);
metadata_visitor::ResolvedValue protocolClassValue = objcVisitor.getValueFor(VMAddress(cacheOptPtrsVMAddr.rawValue()));
protocolClassVMAddr = objcVisitor.resolveRebase(protocolClassValue).vmAddress();
@@ -5798,7 +5007,7 @@
const LayoutBuilder layoutBuilder(cacheDylibs, this->exeInputFiles);
const LayoutBuilder* layoutBuilderPtr = &layoutBuilder;
- STACK_ALLOCATOR(processConfigAlloc, 0);
+ EphemeralAllocator processConfigAlloc;
dyld4::Vector<ProcessConfig::DyldCache::CacheDylib> processConfigDylibsOwner(processConfigAlloc);
auto& processConfigDylibs = processConfigDylibsOwner;
@@ -5868,10 +5077,9 @@
osDelegate._mappedOtherDylibs = otherMapping;
osDelegate._gradedArchs = &this->options.archs;
//osDelegate._dyldCache = dyldCache;
- STACK_ALLOCATOR(alloc, 0);
+ EphemeralAllocator alloc;
ProcessConfig processConfig(&kernArgs, osDelegate, alloc);
- RuntimeLocks locks;
- RuntimeState state(processConfig, locks, alloc);
+ RuntimeState state(processConfig, alloc);
RuntimeState* statePtr = &state;
Diagnostics launchDiag;
@@ -5900,12 +5108,12 @@
char altPath[PATH_MAX];
strlcpy(altPath, "/System/iOSSupport", PATH_MAX);
strlcat(altPath, loadPath, PATH_MAX);
- if ( const dyld4::PrebuiltLoader* ldr = cachedDylibsLoaderSet->findLoader(*statePtr, altPath) )
+ if ( const dyld4::PrebuiltLoader* ldr = cachedDylibsLoaderSet->findLoader(altPath) )
return (const Loader*)ldr;
}
// check if path is a dylib in the dyld cache, then use its PrebuiltLoader
- if ( const dyld4::PrebuiltLoader* ldr = cachedDylibsLoaderSet->findLoader(*statePtr, loadPath) )
+ if ( const dyld4::PrebuiltLoader* ldr = cachedDylibsLoaderSet->findLoader(loadPath) )
return (const Loader*)ldr;
// call through to getLoader() which will expand @paths
@@ -5934,23 +5142,6 @@
// FIXME: Propagate errors
return Error();
}
-
- // Set dylibs to be fixedUp before we partition delay init, as it uses this state
- for ( const Loader* ldr : state.loaded ) {
- if ( const PrebuiltLoader* prebuiltLdr = ldr->isPrebuiltLoader() )
- prebuiltLdr->setFixedUp(state);
- }
-
- // split off delay loaded dylibs into delayLoaded vector
- // We have to do this before making the PrebuiltLoaderSet as objc in the closure needs
- // to know which shared cache dylibs are delay or not.
- STACK_ALLOC_ARRAY(const Loader*, loadersTemp, state.loaded.size());
- for (const Loader* ldr : state.loaded)
- loadersTemp.push_back(ldr);
- std::span<const Loader*> allLoaders(&loadersTemp[0], (size_t)loadersTemp.count());
- std::span<const Loader*> topLoaders = allLoaders.subspan(0, 1);
- state.partitionDelayLoads(allLoaders, topLoaders);
-
state.setMainLoader(mainLoader);
const dyld4::PrebuiltLoaderSet* prebuiltAppSet = dyld4::PrebuiltLoaderSet::makeLaunchSet(launchDiag, state, missingPaths);
if ( launchDiag.hasError() ) {
@@ -6232,9 +5423,9 @@
Diagnostics diag;
// Find the subCache with the hash tables
- cache_builder::ObjCSelectorHashTableChunk* selectorsHashTable = nullptr;
- cache_builder::ObjCClassHashTableChunk* classesHashTable = nullptr;
- cache_builder::ObjCProtocolHashTableChunk* protocolsHashTable = nullptr;
+ cache_builder::ObjCSelectorHashTableChunk* selectorsHashTable = nullptr;
+ cache_builder::ObjCClassHashTableChunk* classesHashTable = nullptr;
+ cache_builder::ObjCProtocolHashTableChunk* protocolsHashTable = nullptr;
for ( SubCache& subCache : this->subCaches ) {
if ( subCache.objcSelectorsHashTable ) {
assert(selectorsHashTable == nullptr);
@@ -6311,32 +5502,15 @@
Timer::Scope timedScope(this->config, "emitObjCHeaderInfo time");
- // We need the prebuilt loaders from the cache dylibs as they contain SectionLocations.
- auto* cachedDylibsLoaderSet = this->prebuiltLoaderBuilder.cachedDylibsLoaderSet;
- assert(cachedDylibsLoaderSet != nullptr);
-
// Emit header info RO
auto* readOnlyList = (ObjCOptimizer::header_info_ro_list_t*)this->objcOptimizer.headerInfoReadOnlyChunk->subCacheBuffer;
readOnlyList->count = (uint32_t)this->objcOptimizer.objcDylibs.size();
readOnlyList->entsize = this->config.layout.is64 ? sizeof(ObjCOptimizer::header_info_ro_64_t) : sizeof(ObjCOptimizer::header_info_ro_32_t);
- // We're also going to populate the objc image info array with the image infos we have here
- auto* imageInfoArray = (objc::objc_image_info*)this->objcOptimizer.imageInfoChunk->subCacheBuffer;
- CacheVMAddress cacheImageInfoBaseAddress = this->objcOptimizer.imageInfoChunk->cacheVMAddress;
- assert(this->objcOptimizer.imageInfoChunk->subCacheFileSize.rawValue() == (readOnlyList->count * sizeof(objc::objc_image_info)));
-
for ( uint32_t i = 0; i != readOnlyList->count; ++i ) {
CacheDylib& cacheDylib = *this->objcOptimizer.objcDylibs[i];
- const uint64_t dyldCategoriesOptimizedFlag = (1 << 0);
- const uint64_t optimizedByDyldFlag = (1 << 3);
-
- // We want the headerinfo_ro_t to point to the imageInfo in the contiguous buffer
- // not the imageinfo in the original dylib
- // Note: We only have standard (8-byte) image infos in the array right now. We'll ignore
- // the array for any elements which use a different sized image info.
- __block CacheVMAddress cacheImageInfoAddress = cacheImageInfoBaseAddress + VMOffset((uint64_t)i * sizeof(objc::objc_image_info));
-
+ __block CacheVMAddress cacheImageInfoAddress;
__block uint8_t* cacheImageInfoBuffer = nullptr;
cacheDylib.forEachCacheSection(^(std::string_view segmentName, std::string_view sectionName,
uint8_t* sectionBuffer, CacheVMAddress sectionVMAddr,
@@ -6346,11 +5520,7 @@
if ( sectionName != "__objc_imageinfo" )
return;
- if ( sectionVMSize.rawValue() != sizeof(objc::objc_image_info) ) {
- // Skip the optimized array and use this element directly
- cacheImageInfoAddress = sectionVMAddr;
- }
-
+ cacheImageInfoAddress = sectionVMAddr;
cacheImageInfoBuffer = sectionBuffer;
stop = true;
});
@@ -6359,12 +5529,6 @@
void* arrayElement = &readOnlyList->arrayBase[0] + (i * readOnlyList->entsize);
CacheVMAddress machHeaderVMAddr = cacheDylib.cacheLoadAddress;
-
- // Get the PrebuiltLoader* for this cache dylib
- const PrebuiltLoader* ldr = cachedDylibsLoaderSet->atIndex(cacheDylib.cacheIndex);
- //assert(ldr->path(state) == cacheDylib.installName); // can't do assert because state is not passed to this method
-
- CacheVMAddress ldrVMAddr = getVMAddressInSection(*this->prebuiltLoaderBuilder.cacheDylibsLoaderChunk, ldr);
if ( this->config.layout.is64 ) {
ObjCOptimizer::header_info_ro_64_t* element = (ObjCOptimizer::header_info_ro_64_t*)arrayElement;
@@ -6382,13 +5546,6 @@
element->info_offset = infoOffset;
// Check for truncation
assert(element->info_offset == infoOffset);
-
- // metadata_offset
- CacheVMAddress metadataOffsetVMAddr = getVMAddressInSection(*this->objcOptimizer.headerInfoReadOnlyChunk, &element->metadata_offset);
- int64_t metadataOffset = ldrVMAddr.rawValue() - metadataOffsetVMAddr.rawValue();
- element->metadata_offset = metadataOffset;
- // Check for truncation
- assert(element->metadata_offset == metadataOffset);
}
else {
ObjCOptimizer::header_info_ro_32_t* element = (ObjCOptimizer::header_info_ro_32_t*)arrayElement;
@@ -6406,25 +5563,15 @@
element->info_offset = (int32_t)infoOffset;
// Check for truncation
assert(element->info_offset == infoOffset);
-
- // metadata_offset
- CacheVMAddress metadataOffsetVMAddr = getVMAddressInSection(*this->objcOptimizer.headerInfoReadOnlyChunk, &element->metadata_offset);
- int64_t metadataOffset = ldrVMAddr.rawValue() - metadataOffsetVMAddr.rawValue();
- element->metadata_offset = (int32_t)metadataOffset;
- // Check for truncation
- assert(element->metadata_offset == metadataOffset);
}
// Set the dylib to be optimized, which lets it use this header info
- objc::objc_image_info* info = (objc::objc_image_info*)cacheImageInfoBuffer;
- info->flags |= optimizedByDyldFlag;
- if ( this->objcCategoryOptimizer.preAttachedDylibs.contains(i) ) {
- if ( !this->objcCategoryOptimizer.excludedDylibs.contains(i) )
- info->flags |= dyldCategoriesOptimizedFlag;
- }
-
- // Also copy in to the contiguous space
- memcpy(&imageInfoArray[i], info, sizeof(objc::objc_image_info));
+ struct objc_image_info {
+ int32_t version;
+ uint32_t flags;
+ };
+ objc_image_info* info = (objc_image_info*)cacheImageInfoBuffer;
+ info->flags = info->flags | (1 << 3);
}
// Emit header info RW
@@ -6607,8 +5754,7 @@
return;
if ( (strncmp(sectInfo.segInfo.segName, "__DATA", 6) != 0)
- && (strncmp(sectInfo.segInfo.segName, "__AUTH", 6) != 0)
- && (strncmp(sectInfo.segInfo.segName, "__TPRO_CONST", 12) != 0))
+ && (strncmp(sectInfo.segInfo.segName, "__AUTH", 6) != 0) )
return;
// Found the section we need. Now to check if its valid
@@ -6823,44 +5969,22 @@
if ( (dataConstRegion == nullptr) && (authConstRegion == nullptr) )
continue;
- for ( UniquedGOTKind sectionKind : { UniquedGOTKind::regular, UniquedGOTKind::authGot, UniquedGOTKind::authPtr } ) {
-
- Region* region = nullptr;
- CoalescedGOTSection* subCacheUniquedGOTs = nullptr;
-
- // Skip sections if their segment doesn't exist
- switch ( sectionKind ) {
- case UniquedGOTKind::regular:
- if ( dataConstRegion == nullptr )
- continue;
-
- region = dataConstRegion;
- subCacheUniquedGOTs = &subCache.uniquedGOTsOptimizer.regularGOTs;
- break;
- case UniquedGOTKind::authGot:
- if ( authConstRegion == nullptr )
- continue;
-
- region = authConstRegion;
- subCacheUniquedGOTs = &subCache.uniquedGOTsOptimizer.authGOTs;
- break;
- case UniquedGOTKind::authPtr:
- if ( authConstRegion == nullptr )
- continue;
-
- region = authConstRegion;
- subCacheUniquedGOTs = &subCache.uniquedGOTsOptimizer.authPtrs;
- break;
- }
-
- if ( subCacheUniquedGOTs->cacheChunk == nullptr )
+ for ( bool auth : { false, true } ) {
+ if ( auth && (authConstRegion == nullptr) )
continue;
-
- UniquedGOTsChunk* subCacheGOTChunk = subCacheUniquedGOTs->cacheChunk->isUniquedGOTsChunk();
+ if ( !auth && (dataConstRegion == nullptr) )
+ continue;
+
+ Region& region = auth ? *authConstRegion : *dataConstRegion;
+ CoalescedGOTSection& subCacheUniquedGOTs = auth ? subCache.uniquedGOTsOptimizer.authGOTs : subCache.uniquedGOTsOptimizer.regularGOTs;
+ if ( subCacheUniquedGOTs.cacheChunk == nullptr )
+ continue;
+
+ UniquedGOTsChunk* subCacheGOTChunk = subCacheUniquedGOTs.cacheChunk->isUniquedGOTsChunk();
std::set<const void*> seenFixups;
std::vector<PatchInfo::GOTInfo> gots;
- for ( const Chunk* chunk : region->chunks ) {
+ for ( const Chunk* chunk : region.chunks ) {
const DylibSegmentChunk* segmentChunk = chunk->isDylibSegmentChunk();
if ( !segmentChunk )
continue;
@@ -6871,26 +5995,19 @@
// Walk all the binds in this dylib, looking for GOT uses of the bind
assert(cacheDylib->bindTargets.size() == dylibPatchInfo.bindGOTUses.size());
assert(cacheDylib->bindTargets.size() == dylibPatchInfo.bindAuthGOTUses.size());
- assert(cacheDylib->bindTargets.size() == dylibPatchInfo.bindAuthPtrUses.size());
for ( uint32_t bindIndex = 0; bindIndex != cacheDylib->bindTargets.size(); ++bindIndex ) {
const CacheDylib::BindTarget& bindTarget = cacheDylib->bindTargets[bindIndex];
- std::span<PatchInfo::GOTInfo> bindUses;
- switch ( sectionKind ) {
- case UniquedGOTKind::regular:
- bindUses = dylibPatchInfo.bindGOTUses[bindIndex];
- break;
- case UniquedGOTKind::authGot:
- bindUses = dylibPatchInfo.bindAuthGOTUses[bindIndex];
- break;
- case UniquedGOTKind::authPtr:
- bindUses = dylibPatchInfo.bindAuthPtrUses[bindIndex];
- break;
+ std::vector<PatchInfo::GOTInfo>* bindUses = nullptr;
+ if ( auth ) {
+ bindUses = &dylibPatchInfo.bindAuthGOTUses[bindIndex];
+ } else {
+ bindUses = &dylibPatchInfo.bindGOTUses[bindIndex];
}
// For absolute binds, just set the pointers and move on
if ( bindTarget.kind == CacheDylib::BindTarget::Kind::absolute ) {
- for ( const PatchInfo::GOTInfo& got : bindUses ) {
+ for ( const PatchInfo::GOTInfo& got : *bindUses ) {
CacheVMAddress gotVMAddr = got.patchInfo.cacheVMAddr;
assert(gotVMAddr >= subCacheGOTChunk->cacheVMAddress);
assert(gotVMAddr < (subCacheGOTChunk->cacheVMAddress + subCacheGOTChunk->cacheVMSize));
@@ -6906,7 +6023,7 @@
continue;
}
- gots.insert(gots.end(), bindUses.begin(), bindUses.end());
+ gots.insert(gots.end(), bindUses->begin(), bindUses->end());
}
}
@@ -6980,8 +6097,7 @@
for ( CacheDylib* cacheDylib : this->objcOptimizer.objcDylibs ) {
objcVisitors.push_back(cacheDylib->makeCacheObjCVisitor(config, nullptr,
- this->objcProtocolOptimizer.canonicalProtocolsChunk,
- this->objcCategoryOptimizer.categoriesChunk));
+ this->objcProtocolOptimizer.canonicalProtocolsChunk));
}
// The offset in the protocol buffer for the next protocol to emit
@@ -7147,7 +6263,7 @@
objcVisitors.reserve(this->cacheDylibs.size());
for ( CacheDylib& cacheDylib : this->cacheDylibs ) {
- objcVisitors.push_back(cacheDylib.makeCacheObjCVisitor(config, nullptr, nullptr, nullptr));
+ objcVisitors.push_back(cacheDylib.makeCacheObjCVisitor(config, nullptr, nullptr));
}
// Check for missing superclasses, but only error on customer/universal caches
@@ -7252,7 +6368,7 @@
worklist.insert(worklist.end(), classInfo->subClasses.begin(), classInfo->subClasses.end());
bool elidedSomething = false;
const objc_visitor::Class& objcClass = classInfo->classPos;
- const bool isSwiftClass = objcClass.isSwift(*classInfo->objcVisitor);
+
auto& map = objcClass.isMetaClass ? metaclassMap : classMap;
std::optional<VMAddress> superclassVMAddr = objcClass.getSuperclassVMAddr(*classInfo->objcVisitor);
@@ -7293,7 +6409,7 @@
continue;
// skip ivars that swiftc has optimized away
- if ( isSwiftClass && ivar.elided(*classInfo->objcVisitor) ) {
+ if ( ivar.elided(*classInfo->objcVisitor) ) {
if ( log ) {
if ( !elidedSomething )
printf("adjusting ivars for %s\n", objcClass.getName(*classInfo->objcVisitor));
@@ -7311,396 +6427,6 @@
objcClass.setInstanceStart(*classInfo->objcVisitor, objcClass.getInstanceStart(*classInfo->objcVisitor) + diff);
objcClass.setInstanceSize(*classInfo->objcVisitor, objcClass.getInstanceSize(*classInfo->objcVisitor) + diff);
}
- }
-
- return Error();
-}
-
-Error SharedCacheBuilder::emitPreAttachedObjCCategories()
-{
-
- if ( this->objcOptimizer.objcDylibs.empty() )
- return Error();
-
- Timer::Scope timedScope(this->config, "dylib emitPreAttachedObjCCategories time");
-
- ObjCPreAttachedCategoriesChunk* categoriesChunk = this->objcCategoryOptimizer.categoriesChunk;
-
- // Build reverse map of categories for fast lookup during optimization
- __block std::unordered_multimap<size_t, uint64_t> dylibsToClasses;
- __block std::unordered_multimap<uint64_t, const ObjCCategoryOptimizer::Category*> classMap;
- __block std::unordered_multimap<uint64_t, const ObjCCategoryOptimizer::Category*> metaClassMap;
- auto& categories = this->objcCategoryOptimizer.categories;
- for (size_t i = 0; i < categories.size(); i++) {
- dylibsToClasses.insert({ categories[i].classDylibIndex.value(), categories[i].classVMAddress.value().rawValue() });
- classMap.insert({ categories[i].classVMAddress.value().rawValue(), &categories[i] });
- }
-
- __block dyld3::MachOFile::PointerMetaData methodListPMD;
- if ( this->config.layout.hasAuthRegion ) {
- methodListPMD.diversity = 0xC310;
- methodListPMD.high8 = 0;
- methodListPMD.authenticated = 1;
- methodListPMD.key = ptrauth_key_asda;
- methodListPMD.usesAddrDiversity = 1;
- }
-
- __block uint64_t chunkOffset = 0;
-
- // The very first thing we want to do is make an empty class method list
- // All classes which have no method lists will have this added as the last entry
- // in their list
- assert(categoriesChunk->subCacheFileSize.rawValue() >= 8);
- void* emptyMethodListLocation = categoriesChunk->subCacheBuffer;
- CacheVMAddress emptyMethodListVMAddr = categoriesChunk->cacheVMAddress;
- chunkOffset += objc_visitor::MethodList::makeEmptyMethodList(emptyMethodListLocation);
-
- auto visitCategoryMetaClass = ^(objc_visitor::Visitor &objCVisitor,
- size_t classCacheIndex, size_t classObjcIndex,
- objc_visitor::Class &objcClass,
- std::span<DylibSegmentChunk> cacheDylibSegments) {
- auto metaClassRange = metaClassMap.equal_range(objcClass.getVMAddress().rawValue());
- if ( metaClassRange.first == metaClassRange.second )
- return;
-
- //fprintf(stderr, "meta class %s %s 0x%llx\n", this->cacheDylibs[classCacheIndex].installName.data(), objcClass.getName(objCVisitor), objcClass.getVMAddress().rawValue());
- // class method lists
- {
- uint8_t* listBuffer = categoriesChunk->subCacheBuffer + chunkOffset;
- uint64_t firstEntryOffset = chunkOffset;
- ListOfListsEntry* listHeader = (ListOfListsEntry*)listBuffer;
- ListOfListsEntry* listEntry = listHeader + 1;
-
- bool createdNewList = false;
- for (auto classEntry = metaClassRange.first; classEntry != metaClassRange.second; classEntry++) {
- const ObjCCategoryOptimizer::Category* category = classEntry->second;
- if ( !category->cMethodListVMAddress.has_value() )
- continue;
-
- //fprintf(stderr, " meta cat %s 0x%llx %s (%llu) => %zu\n", category->name.data(), category->vmAddress.value().rawValue(), this->objcOptimizer.objcDylibs[category->dylibObjcIndex.value()]->installName.data(), category->dylibObjcIndex.value(), classObjcIndex);
- if ( !createdNewList ) {
- createdNewList = true;
- // New list with count and entry size
- listHeader->entsize = sizeof(ListOfListsEntry);
- listHeader->count = 0;
- chunkOffset += sizeof(ListOfListsEntry);
- }
-
- listEntry->imageIndex = category->dylibObjcIndex.value();
- int64_t destVMAddr = (int64_t)category->cMethodListVMAddress.value().rawValue();
- listEntry->offset = destVMAddr - getVMAddressInSection(*categoriesChunk, listEntry).rawValue();
- listEntry++;
- listHeader->count++;
- chunkOffset += sizeof(ListOfListsEntry);
- }
-
- if ( createdNewList ) {
- objc_visitor::MethodList methodList = objcClass.getBaseMethods(objCVisitor);
- // add original method list
- if ( methodList.numMethods() > 0 ) {
- listEntry->imageIndex = classObjcIndex;
- int64_t destVMAddr = (int64_t)methodList.getVMAddress().value().rawValue();
- listEntry->offset = destVMAddr - getVMAddressInSection(*categoriesChunk, listEntry).rawValue();
- } else {
- // add an empty list entry if no original was found
- listEntry->imageIndex = classObjcIndex;
- int64_t destVMAddr = (int64_t)emptyMethodListVMAddr.rawValue();
- listEntry->offset = destVMAddr - getVMAddressInSection(*categoriesChunk, listEntry).rawValue();
- }
- listEntry++;
- listHeader->count++;
- chunkOffset += sizeof(ListOfListsEntry);
- //fprintf(stderr, "Total cMethod list lists: %d\n", listHeader->count);
- // store new base methods pointer to update
- CacheVMAddress newBaseMethods = categoriesChunk->cacheVMAddress + VMOffset(firstEntryOffset);
- metadata_visitor::ResolvedValue field = objcClass.setBaseMethodsVMAddr(objCVisitor,
- VMAddress(newBaseMethods.rawValue() | 1),
- methodListPMD);
- cacheDylibSegments[field.segmentIndex()].tracker.add(field.value());
- }
- }
- // class property lists
- {
- uint8_t* listBuffer = categoriesChunk->subCacheBuffer + chunkOffset;
- uint64_t firstEntryOffset = chunkOffset;
- ListOfListsEntry* listHeader = (ListOfListsEntry*)listBuffer;
- ListOfListsEntry* listEntry = listHeader + 1;
-
- bool createdNewList = false;
- for (auto classEntry = metaClassRange.first; classEntry != metaClassRange.second; classEntry++) {
- const ObjCCategoryOptimizer::Category* category = classEntry->second;
- if ( !category->cPropertyListVMAddress.has_value() )
- continue;
-
- //fprintf(stderr, " meta cat %s 0x%llx %s (%llu) => %zu\n", category->name.data(), category->vmAddress.value().rawValue(), this->objcOptimizer.objcDylibs[category->dylibObjcIndex.value()]->installName.data(), category->dylibObjcIndex.value(), classObjcIndex);
- if ( !createdNewList ) {
- createdNewList = true;
- // New list with count and entry size
- listHeader->entsize = sizeof(ListOfListsEntry);
- listHeader->count = 0;
- chunkOffset += sizeof(ListOfListsEntry);
- }
-
- listEntry->imageIndex = category->dylibObjcIndex.value();
- int64_t destVMAddr = (int64_t)category->cPropertyListVMAddress.value().rawValue();
- listEntry->offset = destVMAddr - getVMAddressInSection(*categoriesChunk, listEntry).rawValue();
- listEntry++;
- listHeader->count++;
- chunkOffset += sizeof(ListOfListsEntry);
- }
-
- if ( createdNewList ) {
- objc_visitor::PropertyList propertyList = objcClass.getBaseProperties(objCVisitor);
- // add original method list
- if ( propertyList.numProperties() > 0 ) {
- listEntry->imageIndex = classObjcIndex;
- int64_t destVMAddr = (int64_t)propertyList.getVMAddress().value().rawValue();
- listEntry->offset = destVMAddr - getVMAddressInSection(*categoriesChunk, listEntry).rawValue();
- } else {
- // add an empty list entry if no original was found
- // Zeroing the entry will make the offset point to itself
- // That will then be interpreted as a ListOfListsEntry of count 0
- // This also means that the image at index 0 needs to always be libobjc.A.dylib
- listEntry->imageIndex = 0;
- listEntry->offset = 0;
- }
- listEntry++;
- listHeader->count++;
- chunkOffset += sizeof(ListOfListsEntry);
-
- //fprintf(stderr, "Total cProperty list lists: %d\n", listHeader->count);
- // store new base properties pointer to update
- CacheVMAddress newBaseProperties = categoriesChunk->cacheVMAddress + VMOffset(firstEntryOffset);
- metadata_visitor::ResolvedValue field = objcClass.setBasePropertiesVMAddr(objCVisitor,
- VMAddress(newBaseProperties.rawValue() | 1));
- cacheDylibSegments[field.segmentIndex()].tracker.add(field.value());
- }
- }
- return;
- };
- auto visitCategoryClass = ^(objc_visitor::Visitor &objCVisitor,
- size_t classCacheIndex, size_t classObjcIndex,
- objc_visitor::Class &objcClass,
- std::span<DylibSegmentChunk> cacheDylibSegments) {
- auto classRange = classMap.equal_range(objcClass.getVMAddress().rawValue());
- if ( classRange.first == classRange.second )
- return;
-
- //fprintf(stderr, "class %s %s 0x%llx\n", this->cacheDylibs[classCacheIndex].installName.data(), objcClass.getName(objCVisitor), objcClass.getVMAddress().rawValue());
- // instance method lists
- {
- uint8_t* listBuffer = categoriesChunk->subCacheBuffer + chunkOffset;
- uint64_t firstEntryOffset = chunkOffset;
- ListOfListsEntry* listHeader = (ListOfListsEntry*)listBuffer;
- ListOfListsEntry* listEntry = listHeader + 1;
-
- bool createdNewList = false;
- for (auto classEntry = classRange.first; classEntry != classRange.second; classEntry++) {
- const ObjCCategoryOptimizer::Category* category = classEntry->second;
- if ( !category->iMethodListVMAddress.has_value() )
- continue;
-
- //fprintf(stderr, " cat %s 0x%llx %s (%llu) => %zu\n", category->name.data(), category->vmAddress.value().rawValue(), this->objcOptimizer.objcDylibs[category->dylibObjcIndex.value()]->installName.data(), category->dylibObjcIndex.value(), classObjcIndex);
- if ( !createdNewList ) {
- createdNewList = true;
- // New list with count and entry size
- listHeader->entsize = sizeof(ListOfListsEntry);
- listHeader->count = 0;
- chunkOffset += sizeof(ListOfListsEntry);
- }
-
- listEntry->imageIndex = category->dylibObjcIndex.value();
- int64_t destVMAddr = (int64_t)category->iMethodListVMAddress.value().rawValue();
- listEntry->offset = destVMAddr - getVMAddressInSection(*categoriesChunk, listEntry).rawValue();
- listEntry++;
- listHeader->count++;
- chunkOffset += sizeof(ListOfListsEntry);
- }
-
- if ( createdNewList ) {
- objc_visitor::MethodList methodList = objcClass.getBaseMethods(objCVisitor);
- // add original method list
- if ( methodList.numMethods() > 0 ) {
- listEntry->imageIndex = classObjcIndex;
- int64_t destVMAddr = (int64_t)methodList.getVMAddress().value().rawValue();
- listEntry->offset = destVMAddr - getVMAddressInSection(*categoriesChunk, listEntry).rawValue();
- } else {
- // add an empty list entry if no original was found
- listEntry->imageIndex = classObjcIndex;
- int64_t destVMAddr = (int64_t)emptyMethodListVMAddr.rawValue();
- listEntry->offset = destVMAddr - getVMAddressInSection(*categoriesChunk, listEntry).rawValue();
- }
- listEntry++;
- listHeader->count++;
- chunkOffset += sizeof(ListOfListsEntry);
- //fprintf(stderr, "Total iMethod list lists: %d\n", listHeader->count);
- // store new base methods pointer to update
- CacheVMAddress newBaseMethods = categoriesChunk->cacheVMAddress + VMOffset(firstEntryOffset);
- metadata_visitor::ResolvedValue field = objcClass.setBaseMethodsVMAddr(objCVisitor,
- VMAddress(newBaseMethods.rawValue() | 1),
- methodListPMD);
- cacheDylibSegments[field.segmentIndex()].tracker.add(field.value());
- }
- }
-
- // protocol lists
- {
- uint8_t* listBuffer = categoriesChunk->subCacheBuffer + chunkOffset;
- uint64_t firstEntryOffset = chunkOffset;
- ListOfListsEntry* listHeader = (ListOfListsEntry*)listBuffer;
- ListOfListsEntry* listEntry = listHeader + 1;
-
- bool createdNewList = false;
- for (auto classEntry = classRange.first; classEntry != classRange.second; classEntry++) {
- const ObjCCategoryOptimizer::Category* category = classEntry->second;
- if ( !category->protocolListVMAddress.has_value() )
- continue;
-
- //fprintf(stderr, " cat %s 0x%llx %s (%llu) => %zu\n", category->name.data(), category->vmAddress.value().rawValue(), this->objcOptimizer.objcDylibs[category->dylibObjcIndex.value()]->installName.data(), category->dylibObjcIndex.value(), classObjcIndex);
- if ( !createdNewList ) {
- createdNewList = true;
- // New list with count and entry size
- listHeader->entsize = sizeof(ListOfListsEntry);
- listHeader->count = 0;
- chunkOffset += sizeof(ListOfListsEntry);
- }
-
- listEntry->imageIndex = category->dylibObjcIndex.value();
- int64_t destVMAddr = (int64_t)category->protocolListVMAddress.value().rawValue();
- listEntry->offset = destVMAddr - getVMAddressInSection(*categoriesChunk, listEntry).rawValue();
- listEntry++;
- listHeader->count++;
- chunkOffset += sizeof(ListOfListsEntry);
- }
-
- if ( createdNewList ) {
- objc_visitor::ProtocolList protocolList = objcClass.getBaseProtocols(objCVisitor);
- // add original protocol list
- if ( protocolList.numProtocols(objCVisitor) > 0 ) {
- listEntry->imageIndex = classObjcIndex;
- int64_t destVMAddr = (int64_t)protocolList.getVMAddress().value().rawValue();
- listEntry->offset = destVMAddr - getVMAddressInSection(*categoriesChunk, listEntry).rawValue();
- } else {
- // add an empty list entry if no original was found
- // Zeroing the entry will make the offset point to itself
- // That will then be interpreted as a ListOfListsEntry of count 0
- // This also means that the image at index 0 needs to always be libobjc.A.dylib
- listEntry->imageIndex = 0;
- listEntry->offset = 0;
- }
- listEntry++;
- listHeader->count++;
- chunkOffset += sizeof(ListOfListsEntry);
-
- //fprintf(stderr, "Total Protocol list lists: %d\n", listHeader->count);
- // store new base protocols pointer to update
- CacheVMAddress newBaseProtocols = categoriesChunk->cacheVMAddress + VMOffset(firstEntryOffset);
- metadata_visitor::ResolvedValue field = objcClass.setBaseProtocolsVMAddr(objCVisitor,
- VMAddress(newBaseProtocols.rawValue() | 1));
- cacheDylibSegments[field.segmentIndex()].tracker.add(field.value());
- }
- }
-
- // instance properties
- {
- uint8_t* listBuffer = categoriesChunk->subCacheBuffer + chunkOffset;
- uint64_t firstEntryOffset = chunkOffset;
- ListOfListsEntry* listHeader = (ListOfListsEntry*)listBuffer;
- ListOfListsEntry* listEntry = listHeader + 1;
-
- bool createdNewList = false;
- for (auto classEntry = classRange.first; classEntry != classRange.second; classEntry++) {
- const ObjCCategoryOptimizer::Category* category = classEntry->second;
- if ( !category->iPropertyListVMAddress.has_value() )
- continue;
-
- //fprintf(stderr, " cat %s 0x%llx %s (%llu) => %zu\n", category->name.data(), category->vmAddress.value().rawValue(), this->objcOptimizer.objcDylibs[category->dylibObjcIndex.value()]->installName.data(), category->dylibObjcIndex.value(), classObjcIndex);
- if ( !createdNewList ) {
- createdNewList = true;
- // New list with count and entry size
- listHeader->entsize = sizeof(ListOfListsEntry);
- listHeader->count = 0;
- chunkOffset += sizeof(ListOfListsEntry);
- }
-
- listEntry->imageIndex = category->dylibObjcIndex.value();
- int64_t destVMAddr = (int64_t)category->iPropertyListVMAddress.value().rawValue();
- listEntry->offset = destVMAddr - getVMAddressInSection(*categoriesChunk, listEntry).rawValue();
- listEntry++;
- listHeader->count++;
- chunkOffset += sizeof(ListOfListsEntry);
- }
-
- if ( createdNewList ) {
- objc_visitor::PropertyList propertyList = objcClass.getBaseProperties(objCVisitor);
- // add original property list
- if ( propertyList.numProperties() > 0 ) {
- listEntry->imageIndex = classObjcIndex;
- int64_t destVMAddr = (int64_t)propertyList.getVMAddress().value().rawValue();
- listEntry->offset = destVMAddr - getVMAddressInSection(*categoriesChunk, listEntry).rawValue();
- } else {
- // add an empty list entry if no original was found
- // Zeroing the entry will make the offset point to itself
- // That will then be interpreted as a ListOfListsEntry of count 0
- // This also means that the image at index 0 needs to always be libobjc.A.dylib
- listEntry->imageIndex = 0;
- listEntry->offset = 0;
- }
- listEntry++;
- listHeader->count++;
- chunkOffset += sizeof(ListOfListsEntry);
- //fprintf(stderr, "Total iProperty list lists: %d\n", listHeader->count);
- // store new base properties pointer to update
- CacheVMAddress newBaseProperties = categoriesChunk->cacheVMAddress + VMOffset(firstEntryOffset);
- metadata_visitor::ResolvedValue field = objcClass.setBasePropertiesVMAddr(objCVisitor,
- VMAddress(newBaseProperties.rawValue() | 1));
- cacheDylibSegments[field.segmentIndex()].tracker.add(field.value());
- }
- }
-
- // class method lists and class properties
- {
- // check if we need to update the meta class
- bool isPatchableClass;
- VMAddress metaClassVMAddr = objcClass.getISA(objCVisitor, isPatchableClass).vmAddress();
-
- bool visitMetaClassMap = false;
- for (auto classEntry = classRange.first; classEntry != classRange.second; classEntry++) {
- const ObjCCategoryOptimizer::Category* category = classEntry->second;
- if ( category->cPropertyListVMAddress.has_value() || category->cMethodListVMAddress.has_value() ) {
- metaClassMap.insert({ metaClassVMAddr.rawValue(), category });
- visitMetaClassMap = true;
- }
- }
- if ( visitMetaClassMap ) {
- metadata_visitor::ResolvedValue metaClassValue = objCVisitor.getValueFor(metaClassVMAddr);
- objc_visitor::Class objcMetaClass(metaClassValue, /*isMetaClass*/ true, /*isPatchable*/ false);
- visitCategoryMetaClass(objCVisitor, classCacheIndex, classObjcIndex, objcMetaClass, cacheDylibSegments);
- }
- }
- };
-
- size_t objcIndex = 0;
- for (size_t cacheIndex = 0; cacheIndex < this->cacheDylibs.size(); cacheIndex++) {
- CacheDylib& cacheDylib = this->cacheDylibs[cacheIndex];
- if ( !cacheDylib.inputMF->hasObjC() )
- continue;
-
- this->objcCategoryOptimizer.preAttachedDylibs.insert(objcIndex);
- __block objc_visitor::Visitor objCVisitor = cacheDylib.makeCacheObjCVisitor(config, nullptr, nullptr, categoriesChunk);
-
- // dylibsToClasses can contain multiple entries with the same pair of key/values
- std::set<uint64_t> visitedClasses;
- auto dylibToClassRange = dylibsToClasses.equal_range(cacheIndex);
- for (auto dylibToClass = dylibToClassRange.first; dylibToClass != dylibToClassRange.second; dylibToClass++) {
- if ( visitedClasses.contains(dylibToClass->second) )
- continue;
- visitedClasses.insert(dylibToClass->second);
- metadata_visitor::ResolvedValue classValue = objCVisitor.getValueFor(VMAddress(dylibToClass->second));
- objc_visitor::Class objcClass(classValue, /*isMetaClass*/ false, /*isPatchable*/ false);
- visitCategoryClass(objCVisitor, cacheIndex, objcIndex, objcClass, cacheDylib.segments);
- }
- objcIndex++;
}
return Error();
@@ -7735,13 +6461,13 @@
Diagnostics diag;
auto objcClassOpt = (objc::ClassHashTable*)this->objcClassOptimizer.classHashTableChunk->subCacheBuffer;
- buildSwiftHashTables(this->config, diag, this->objcOptimizer.objcDylibs,
+ buildSwiftHashTables(this->config, diag, this->cacheDylibs,
extraRegions, objcClassOpt,
this->objcOptimizer.headerInfoReadOnlyChunk->subCacheBuffer,
this->objcOptimizer.headerInfoReadWriteChunk->subCacheBuffer,
this->objcOptimizer.headerInfoReadOnlyChunk->cacheVMAddress,
- swiftPrespecializedDylib,
this->swiftProtocolConformanceOptimizer);
+
if ( diag.hasError() )
return Error("Couldn't build Swift protocol opts because: %s", diag.errorMessageCStr());
@@ -7762,7 +6488,7 @@
Timer::Scope timedScope(this->config, "computeSlideInfo time");
if ( !this->config.slideInfo.slideInfoFormat.has_value() ) {
- assert(this->options.isSimulator());
+ assert(this->options.isSimultor());
}
Error err = parallel::forEach(this->subCaches, ^(size_t index, SubCache& subCache) {
@@ -7798,14 +6524,11 @@
switch ( region.kind ) {
case Region::Kind::text:
case Region::Kind::dynamicConfig:
- case Region::Kind::readOnly:
case Region::Kind::linkedit:
maxSlide = std::min(maxSlide, subCacheLimit - region.subCacheVMSize);
break;
- case Region::Kind::tproConst:
case Region::Kind::data:
case Region::Kind::dataConst:
- case Region::Kind::tproAuthConst:
case Region::Kind::auth:
case Region::Kind::authConst:
if ( firstDataRegion == nullptr )
@@ -7830,11 +6553,6 @@
// We must be a largeContiguous cache. Others were dealt with above in the x86_64 and/or sim cases
assert(this->config.layout.contiguous.has_value());
- // Some caches have a fixed max slide
- if ( this->config.layout.cacheFixedSlide.has_value() ) {
- return this->config.layout.cacheFixedSlide.value();
- }
-
// Start off making sure we can't slide past the end of the cache
CacheVMAddress maxVMAddress(0ULL);
for ( const Region& region : this->subCaches.back().regions ) {
@@ -7865,10 +6583,6 @@
if ( sizeUpToTextEnd <= twoGB )
maxSlide = CacheVMSize(twoGB - sizeUpToTextEnd);
- if ( this->config.layout.cacheMaxSlide.has_value() ) {
- maxSlide = std::min(maxSlide, CacheVMSize(this->config.layout.cacheMaxSlide.value()));
- }
-
return maxSlide.rawValue();
}
@@ -7882,35 +6596,10 @@
Diagnostics diag;
cacheDylib.addObjcSegments(diag, aggregateTimer,
this->objcOptimizer.headerInfoReadOnlyChunk,
- this->objcOptimizer.imageInfoChunk,
this->objcProtocolOptimizer.protocolHashTableChunk,
- this->objcCategoryOptimizer.categoriesChunk,
this->objcOptimizer.headerInfoReadWriteChunk,
this->objcProtocolOptimizer.canonicalProtocolsChunk);
}
-}
-
-Error SharedCacheBuilder::patchLinkedDylibs(CacheDylib& cacheDylib)
-{
- if ( swiftPrespecializedDylib == nullptr )
- return Error::none();
-
- Timer::Scope timedScope(this->config, "patchLinkedDylibs time");
- Timer::AggregateTimer aggregateTimerOwner(this->config);
-
- Diagnostics diag;
- if ( &cacheDylib == swiftPrespecializedDylib ) {
- // remove all but libSystem
- cacheDylib.removeLinkedDylibs(diag);
- } else if ( cacheDylib.installName.find("libswiftCore.dylib") != std::string_view::npos ) {
- // add Swift prespecialized dylib dependency to libswiftCore
- cacheDylib.addLinkedDylib(diag, *swiftPrespecializedDylib);
- }
-
- if ( diag.hasError() )
- return Error("%s", diag.errorMessageCStr());
-
- return Error::none();
}
void SharedCacheBuilder::computeCacheHeaders()
@@ -8041,16 +6730,6 @@
return buff;
}
-std::span<const std::string_view> SharedCacheBuilder::getEvictedDylibs() const
-{
- return this->evictedDylibs;
-}
-
-std::string_view SharedCacheBuilder::getSwiftPrespecializedDylibBuildError() const
-{
- return swiftPrespecializedDylibBuildError;
-}
-
void SharedCacheBuilder::getResults(std::vector<CacheBuffer>& results) const
{
for ( const SubCache& subCache : this->subCaches ) {
@@ -8088,15 +6767,12 @@
case Region::Kind::text:
prot = "EX";
break;
- case Region::Kind::tproConst:
case Region::Kind::data:
case Region::Kind::dataConst:
- case Region::Kind::tproAuthConst:
case Region::Kind::auth:
case Region::Kind::authConst:
prot = "RW";
break;
- case Region::Kind::readOnly:
case Region::Kind::linkedit:
prot = "RO";
break;