Loading...
--- dyld/dyld-1066.10/cache_builder/SubCache.cpp
+++ dyld/dyld-1330/cache_builder/SubCache.cpp
@@ -25,8 +25,10 @@
#include "BuilderConfig.h"
#include "BuilderOptions.h"
#include "CacheDylib.h"
+#include "Chunk.h"
#include "CodeSigningTypes.h"
#include "DyldSharedCache.h"
+#include "Header.h"
#include "SubCache.h"
#include "JSONWriter.h"
@@ -36,8 +38,10 @@
#include <CommonCrypto/CommonDigest.h>
#include <CommonCrypto/CommonDigestSPI.h>
-using dyld3::GradedArchs;
using dyld3::MachOFile;
+
+using mach_o::Header;
+using mach_o::Platform;
using error::Error;
@@ -54,24 +58,59 @@
// MARK: --- SharedCacheBuilder::Region methods ---
uint32_t Region::initProt() const
+{
+ uint32_t initProt = 0;
+ switch ( this->kind ) {
+ case Region::Kind::text:
+ initProt = VM_PROT_READ | VM_PROT_EXECUTE;
+ break;
+ case Region::Kind::data:
+ case Region::Kind::auth:
+ initProt = VM_PROT_READ | VM_PROT_WRITE;
+ break;
+ case Region::Kind::dataConst:
+ case Region::Kind::authConst:
+ case Region::Kind::tproConst:
+ case Region::Kind::tproAuthConst:
+ initProt = VM_PROT_READ;
+ break;
+ case Region::Kind::readOnly:
+ case Region::Kind::linkedit:
+ initProt = VM_PROT_READ;
+ break;
+ case Region::Kind::dynamicConfig:
+ // HACK: This is not actually mapped, but it is used in vm calculations
+ initProt = VM_PROT_READ;
+ break;
+ case Region::Kind::unmapped:
+ case Region::Kind::codeSignature:
+ // This isn't mapped, so we should never ask for its initProt
+ assert(0);
+ break;
+ case Region::Kind::numKinds:
+ assert(0);
+ break;
+ }
+
+ return initProt;
+}
+
+uint32_t Region::maxProt() const
{
uint32_t maxProt = 0;
switch ( this->kind ) {
case Region::Kind::text:
maxProt = VM_PROT_READ | VM_PROT_EXECUTE;
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:
maxProt = VM_PROT_READ | VM_PROT_WRITE;
break;
- case Region::Kind::dataConst:
- maxProt = VM_PROT_READ;
- break;
- case Region::Kind::auth:
- maxProt = VM_PROT_READ | VM_PROT_WRITE;
- break;
- case Region::Kind::authConst:
- maxProt = VM_PROT_READ;
- break;
+ case Region::Kind::readOnly:
case Region::Kind::linkedit:
maxProt = VM_PROT_READ;
break;
@@ -92,45 +131,6 @@
return maxProt;
}
-uint32_t Region::maxProt() const
-{
- uint32_t maxProt = 0;
- switch ( this->kind ) {
- case Region::Kind::text:
- maxProt = VM_PROT_READ | VM_PROT_EXECUTE;
- break;
- case Region::Kind::data:
- maxProt = VM_PROT_READ | VM_PROT_WRITE;
- break;
- case Region::Kind::dataConst:
- maxProt = VM_PROT_READ | VM_PROT_WRITE;
- break;
- case Region::Kind::auth:
- maxProt = VM_PROT_READ | VM_PROT_WRITE;
- break;
- case Region::Kind::authConst:
- maxProt = VM_PROT_READ | VM_PROT_WRITE;
- break;
- case Region::Kind::linkedit:
- maxProt = VM_PROT_READ;
- break;
- case Region::Kind::dynamicConfig:
- // HACK: This is not actually mapped, but it is used in vm calculations
- maxProt = VM_PROT_READ;
- break;
- case Region::Kind::unmapped:
- case Region::Kind::codeSignature:
- // This isn't mapped, so we should never ask for its maxprot
- assert(0);
- break;
- case Region::Kind::numKinds:
- assert(0);
- break;
- }
-
- return maxProt;
-}
-
bool Region::canContainAuthPointers() const
{
bool result = false;
@@ -139,14 +139,19 @@
// We should never ask this region if it has auth content
assert(0);
break;
+ case Region::Kind::tproConst:
+ case Region::Kind::tproAuthConst:
case Region::Kind::data:
case Region::Kind::dataConst:
+ // Note TPRO never contains authenticated fixups, so its name is wrong
+ // but its really there to be placed next to the AUTH regions
result = false;
break;
case Region::Kind::auth:
case Region::Kind::authConst:
result = true;
break;
+ case Region::Kind::readOnly:
case Region::Kind::linkedit:
case Region::Kind::dynamicConfig:
// We should never ask this region if it has auth content
@@ -170,10 +175,13 @@
{
switch ( this->kind ) {
case Region::Kind::text:
+ case Region::Kind::tproConst:
case Region::Kind::data:
case Region::Kind::dataConst:
+ case Region::Kind::tproAuthConst:
case Region::Kind::auth:
case Region::Kind::authConst:
+ case Region::Kind::readOnly:
case Region::Kind::linkedit:
return true;
case Region::Kind::unmapped:
@@ -190,10 +198,13 @@
{
switch ( this->kind ) {
case Region::Kind::text:
+ case Region::Kind::tproConst:
case Region::Kind::data:
case Region::Kind::dataConst:
+ case Region::Kind::tproAuthConst:
case Region::Kind::auth:
case Region::Kind::authConst:
+ case Region::Kind::readOnly:
case Region::Kind::linkedit:
case Region::Kind::dynamicConfig:
return true;
@@ -221,10 +232,16 @@
bool nextIsRW = (next.initProt() & VM_PROT_WRITE) != 0;
return nextIsRW;
}
+ case Region::Kind::tproConst:
+ case Region::Kind::tproAuthConst:
case Region::Kind::data:
case Region::Kind::auth: {
// HACK: Remove once we have rdar://96315050
if ( (this->kind == Region::Kind::auth) && (next.kind == Region::Kind::authConst) )
+ return false;
+
+ // Don't add adding from data to tproConst as we didn't have padding from data to auth before
+ if ( (next.kind == Kind::tproConst) || (next.kind == Kind::tproAuthConst) )
return false;
// Add padding if DATA is adjacent to something immutable, eg TEXT/DATA_CONST
@@ -233,12 +250,17 @@
}
case Region::Kind::dataConst:
case Region::Kind::authConst: {
+ // Always add padding from DATA_CONST to TPRO_CONST as TPRO_CONST will actually be dirtied
+ if ( (next.kind == Kind::tproConst) || (next.kind == Kind::tproAuthConst) )
+ return true;
+
// Don't add padding if *_CONST is next to *_CONST, otherwise add padding
bool nextInitIsRO = (next.initProt() & VM_PROT_WRITE) == 0;
bool nextMaxIsRW = (next.maxProt() & VM_PROT_WRITE) != 0;
bool nextIsDataConst = nextInitIsRO & nextMaxIsRW;
return !nextIsDataConst;
}
+ case Region::Kind::readOnly:
case Region::Kind::linkedit:
case Region::Kind::dynamicConfig: {
// Add padding if LINKEDIT is adjacent to something that is mutable,
@@ -321,10 +343,40 @@
switch ( region.kind ) {
case cache_builder::Region::Kind::text:
break;
+ case cache_builder::Region::Kind::tproConst:
+ case cache_builder::Region::Kind::tproAuthConst:
case cache_builder::Region::Kind::dataConst:
case cache_builder::Region::Kind::data:
case cache_builder::Region::Kind::auth:
case cache_builder::Region::Kind::authConst:
+ return true;
+ case Region::Kind::readOnly:
+ case cache_builder::Region::Kind::linkedit:
+ case cache_builder::Region::Kind::unmapped:
+ case cache_builder::Region::Kind::dynamicConfig:
+ case cache_builder::Region::Kind::codeSignature:
+ case cache_builder::Region::Kind::numKinds:
+ break;
+ }
+ }
+ return false;
+}
+
+static bool hasReadOnlyRegion(std::span<Region> regions)
+{
+ for ( const Region& region : regions ) {
+ if ( region.chunks.empty() )
+ continue;
+ switch ( region.kind ) {
+ case cache_builder::Region::Kind::text:
+ case cache_builder::Region::Kind::tproConst:
+ case cache_builder::Region::Kind::tproAuthConst:
+ case cache_builder::Region::Kind::dataConst:
+ case cache_builder::Region::Kind::data:
+ case cache_builder::Region::Kind::auth:
+ case cache_builder::Region::Kind::authConst:
+ break;
+ case Region::Kind::readOnly:
return true;
case cache_builder::Region::Kind::linkedit:
case cache_builder::Region::Kind::unmapped:
@@ -344,10 +396,13 @@
continue;
switch ( region.kind ) {
case cache_builder::Region::Kind::text:
+ case cache_builder::Region::Kind::tproConst:
+ case cache_builder::Region::Kind::tproAuthConst:
case cache_builder::Region::Kind::dataConst:
case cache_builder::Region::Kind::data:
case cache_builder::Region::Kind::auth:
case cache_builder::Region::Kind::authConst:
+ case Region::Kind::readOnly:
break;
case cache_builder::Region::Kind::linkedit:
return true;
@@ -361,43 +416,46 @@
return false;
}
-void SubCache::setSuffix(dyld3::Platform platform, bool forceDevelopmentSubCacheSuffix,
- size_t subCacheIndex)
+void SubCache::setSuffix(Platform platform, bool forceDevelopmentSubCacheSuffix, size_t subCacheIndex)
{
assert(this->isSubCache() || this->isStubsCache());
assert(subCacheIndex > 0);
const char* dataSuffix = forceDevelopmentSubCacheSuffix ? ".development.dylddata" : ".dylddata";
const char* linkeditSuffix = forceDevelopmentSubCacheSuffix ? ".development.dyldlinkedit" : ".dyldlinkedit";
+ const char* readonlySuffix = forceDevelopmentSubCacheSuffix ? ".development.dyldreadonly" : ".dyldreadonly";
const char* subCacheSuffix = forceDevelopmentSubCacheSuffix ? ".development" : "";
- if ( platform == dyld3::Platform::macOS ) {
- // macOS never has a .development suffix
- this->fileSuffix = "." + dyld3::json::decimal(subCacheIndex);
- } else if ( platform == dyld3::Platform::driverKit ) {
+ if ( (platform == Platform::macOS) || platform.isSimulator() ) {
+ // macOS/sims never has a .development suffix
+ this->fileSuffix = "." + json::decimal(subCacheIndex);
+ } else if ( platform == Platform::driverKit ) {
// driverKit never has a .development suffix
- this->fileSuffix = "." + dyld3::json::decimal(subCacheIndex);
+ this->fileSuffix = "." + json::decimal(subCacheIndex);
} else if ( this->isStubsDevelopmentCache() ) {
// Dev stubs always have a suffix
- this->fileSuffix = "." + dyld3::json::decimal(subCacheIndex) + ".development";
+ this->fileSuffix = "." + json::decimal(subCacheIndex) + ".development";
} else if ( this->isStubsCustomerCache() ) {
// Customer stubs never have a suffix
- this->fileSuffix = "." + dyld3::json::decimal(subCacheIndex);
+ this->fileSuffix = "." + json::decimal(subCacheIndex);
} else if ( hasDataRegion(this->regions) ) {
// Data only subcaches have their own suffix
- this->fileSuffix = "." + dyld3::json::decimal(subCacheIndex) + dataSuffix;
+ this->fileSuffix = "." + json::decimal(subCacheIndex) + dataSuffix;
+ } else if ( hasReadOnlyRegion(this->regions) ) {
+ // read-only only subcaches have their own suffix
+ this->fileSuffix = "." + json::decimal(subCacheIndex) + readonlySuffix;
} else if ( hasLinkeditRegion(this->regions) ) {
// Linkedit only subcaches have their own suffix
- this->fileSuffix = "." + dyld3::json::decimal(subCacheIndex) + linkeditSuffix;
+ this->fileSuffix = "." + json::decimal(subCacheIndex) + linkeditSuffix;
} else {
- this->fileSuffix = "." + dyld3::json::decimal(subCacheIndex) + subCacheSuffix;
+ this->fileSuffix = "." + json::decimal(subCacheIndex) + subCacheSuffix;
}
}
static std::string getCodeSigningIdentifier(const BuilderOptions& options)
{
std::string cacheIdentifier = "com.apple.dyld.cache.";
- cacheIdentifier += options.archs.name();
+ cacheIdentifier += options.arch.name();
if ( options.dylibsRemovedFromDisk ) {
switch ( options.kind ) {
case CacheKind::development:
@@ -685,6 +743,14 @@
// Note: cdHash is defined as first 20 bytes of hash
memcpy(this->cdHash, fullCdHash, 20);
+ if ( layout.agile ) {
+ // hash of entire code directory (cdHash) uses same hash as each page
+ uint8_t altfullCdHash[CS_HASH_SIZE_SHA256];
+ CCDigest(kCCDigestSHA256, (const uint8_t*)cd256, layout.cd256Size, altfullCdHash);
+ // Note: cdHash is defined as first 20 bytes of hash
+ memcpy(this->agilecdHash, altfullCdHash, 20);
+ }
+
// Set the UUID string in the subcache
uuid_unparse_upper(dyldCacheHeader->uuid, this->uuidString);
}
@@ -700,6 +766,20 @@
this->regions[(uint32_t)Region::Kind::text].chunks.push_back(chunk);
}
+void SubCache::addTPROConstChunk(const BuilderConfig& config, Chunk* chunk)
+{
+ // Rosetta can't handle an extra shared cache mapping, so use DATA instead
+ // We use DATA instead of DATA_CONST because we don't want the MemoryManager
+ // and DataConstWriter to both mprotect the same pages. But the DataConstWriter
+ // will ignore everything in DATA while the MemoryManager will find TPRO there
+ if ( config.layout.discontiguous.has_value() )
+ this->addDataChunk(chunk);
+ else if ( config.layout.hasAuthRegion )
+ this->regions[(uint32_t)Region::Kind::tproAuthConst].chunks.push_back(chunk);
+ else
+ this->regions[(uint32_t)Region::Kind::tproConst].chunks.push_back(chunk);
+}
+
void SubCache::addDataChunk(Chunk* chunk)
{
this->regions[(uint32_t)Region::Kind::data].chunks.push_back(chunk);
@@ -720,6 +800,15 @@
this->regions[(uint32_t)Region::Kind::authConst].chunks.push_back(chunk);
}
+void SubCache::addReadOnlyChunk(const BuilderConfig& config, Chunk* chunk)
+{
+ // Rosetta can't handle an extra shared cache mapping, so use __TEXT instead
+ if ( config.layout.discontiguous.has_value() )
+ this->addTextChunk(chunk);
+ else
+ this->regions[(uint32_t)Region::Kind::readOnly].chunks.push_back(chunk);
+}
+
void SubCache::addLinkeditChunk(Chunk* chunk)
{
this->regions[(uint32_t)Region::Kind::linkedit].chunks.push_back(chunk);
@@ -733,28 +822,11 @@
void SubCache::addCodeSignatureChunk(Chunk* chunk)
{
this->regions[(uint32_t)Region::Kind::codeSignature].chunks.push_back(chunk);
-}
-
-// HACK: We need to insert the libobjc __TEXT first so that its before all the other OBJC_RO chunks
-void SubCache::addObjCTextChunk(Chunk* chunk)
-{
- std::vector<Chunk*>& chunks = this->regions[(uint32_t)Region::Kind::text].chunks;
- chunks.insert(chunks.begin(), chunk);
-}
-
-// ObjC optimizations need to add read-only chunks. For now these are added to the start
-// of TEXT, so that they are in the same subCache when split by One Cache. In future we want to
-// move these to LINKEDIT
-void SubCache::addObjCReadOnlyChunk(Chunk* chunk)
-{
- std::vector<Chunk*>& chunks = this->regions[(uint32_t)Region::Kind::text].chunks;
- chunks.insert(chunks.begin(), chunk);
}
// All objc optimizations need to be contiguous, but that means if any need AUTH then all do
void SubCache::addObjCReadWriteChunk(const BuilderConfig& config, Chunk* chunk)
{
- // Add canonical objc protocols
if ( config.layout.hasAuthRegion ) {
addAuthChunk(chunk);
}
@@ -763,18 +835,17 @@
}
}
-void SubCache::addDylib(CacheDylib& cacheDylib, bool addLinkedit)
+void SubCache::addDylib(const BuilderConfig& config, CacheDylib& cacheDylib)
{
for ( DylibSegmentChunk& segmentInfo : cacheDylib.segments ) {
switch ( segmentInfo.kind ) {
case Chunk::Kind::dylibText:
- if ( cacheDylib.installName == "/usr/lib/libobjc.A.dylib" )
- this->addObjCTextChunk(&segmentInfo);
- else
- this->addTextChunk(&segmentInfo);
+ this->addTextChunk(&segmentInfo);
+ break;
+ case Chunk::Kind::tproDataConst:
+ this->addTPROConstChunk(config, &segmentInfo);
break;
case Chunk::Kind::dylibData:
- case Chunk::Kind::dylibDataConstWorkaround:
this->addDataChunk(&segmentInfo);
break;
case Chunk::Kind::dylibDataConst:
@@ -782,21 +853,19 @@
break;
case Chunk::Kind::dylibDataDirty:
// On arm64e, dataDirty goes in to auth
- if ( cacheDylib.inputMF->isArch("arm64e") )
+ if ( cacheDylib.inputHdr->isArch("arm64e") )
this->addAuthChunk(&segmentInfo);
else
this->addDataChunk(&segmentInfo);
break;
case Chunk::Kind::dylibAuth:
- case Chunk::Kind::dylibAuthConstWorkaround:
this->addAuthChunk(&segmentInfo);
break;
case Chunk::Kind::dylibAuthConst:
this->addAuthConstChunk(&segmentInfo);
break;
case Chunk::Kind::dylibReadOnly:
- // FIXME: Read-only data should really be in a read-only mapping.
- this->addTextChunk(&segmentInfo);
+ this->addReadOnlyChunk(config, &segmentInfo);
break;
case Chunk::Kind::dylibLinkedit:
// Skip adding here. We'll do this in addLinkeditFromDylib()
@@ -806,9 +875,6 @@
break;
}
}
-
- if ( addLinkedit )
- this->addLinkeditFromDylib(cacheDylib);
}
// Linkedit is stored in Chunks in its own array on the dylib. This adds it to the subCache.
@@ -822,19 +888,102 @@
this->addLinkeditChunk(&chunk);
}
-void SubCache::addCacheHeaderChunk(const std::span<CacheDylib> cacheDylibs)
+void SubCache::forEachTPRORegionInData(SubCache* mainSubCache, std::span<SubCache*> subCaches,
+ void (^callback)(Region& region, const Chunk* firstChunk, const Chunk* lastChunk))
+{
+ // Find all subcaches with TPRO regions, or on x86_64 its those with DATA in them
+ auto runOnSubCache = ^(SubCache* subCache) {
+ for ( Region& region : subCache->regions ) {
+ if ( region.kind != Region::Kind::data )
+ continue;
+
+ const Chunk* firstTPROChunk = nullptr;
+ const Chunk* lastTPROChunk = nullptr;
+ for ( const Chunk* chunk : region.chunks ) {
+ if ( chunk->isTPROChunk() ) {
+ if ( !firstTPROChunk ) {
+ firstTPROChunk = chunk;
+ lastTPROChunk = chunk;
+ } else {
+ lastTPROChunk = chunk;
+ }
+ } else if ( firstTPROChunk ) {
+ // HACK: If we find an alignment chunk then add it as-if its a TPRO chunk. This is to
+ // make sure callers can get page-aligned TPRO regions
+ if ( const AlignChunk* alignChunk = chunk->isAlignChunk() ) {
+ if ( alignChunk->alignment() >= 4_KB ) {
+ lastTPROChunk = chunk;
+ continue;
+ }
+ }
+
+ // Found a non-tpro chunk. Break here and do the callback outside the chunk loop
+ // We don't need to continue the loop as we sorted all the TPRO to be adjacent
+ break;
+ }
+ }
+
+ if ( firstTPROChunk ) {
+ // Ended with a tpro chunk. Make the callback for it
+ callback(region, firstTPROChunk, lastTPROChunk);
+ }
+ }
+ };
+
+ runOnSubCache(mainSubCache);
+ for ( SubCache* subCache : subCaches )
+ runOnSubCache(subCache);
+}
+
+static void forEachTPRORegion(const BuilderConfig& config, SubCache* mainSubCache,
+ std::span<SubCache*> subCaches,
+ void (^callback)(CacheVMAddress firstChunkAddress, CacheVMAddress lastChunkAddress, CacheVMSize lastChunkSize))
+{
+ // Find all subcaches with TPRO regions, or on x86_64 its those with DATA in them
+ if ( config.layout.tproIsInData ) {
+ SubCache::forEachTPRORegionInData(mainSubCache, subCaches, ^(Region& region, const Chunk *firstChunk, const Chunk *lastChunk) {
+ callback(firstChunk->cacheVMAddress, lastChunk->cacheVMAddress, lastChunk->cacheVMSize);
+ });
+ return;
+ }
+
+ auto runOnSubCache = ^(const SubCache* subCache) {
+ for ( const Region& region : subCache->regions ) {
+ if ( (region.kind == Region::Kind::tproConst) || (region.kind == Region::Kind::tproAuthConst) )
+ callback(region.subCacheVMAddress, region.subCacheVMAddress, region.subCacheVMSize);
+ }
+ };
+
+ runOnSubCache(mainSubCache);
+ for ( const SubCache* subCache : subCaches )
+ runOnSubCache(subCache);
+}
+
+static uint32_t numTPRORegions(const BuilderConfig& config, SubCache* mainSubCache,
+ std::span<SubCache*> subCaches)
+{
+ __block uint32_t count = 0;
+ forEachTPRORegion(config, mainSubCache, subCaches, ^(CacheVMAddress, CacheVMAddress, CacheVMSize) {
+ ++count;
+ });
+ return count;
+}
+
+void SubCache::addCacheHeaderChunk(const BuilderOptions& options, const BuilderConfig& config,
+ const std::span<CacheDylib> cacheDylibs)
{
// calculate size of header info and where first dylib's mach_header should start
uint64_t numMappings = this->regions.size();
uint64_t startOffset = sizeof(dyld_cache_header) + (numMappings * sizeof(dyld_cache_mapping_info));
startOffset += numMappings * sizeof(dyld_cache_mapping_and_slide_info);
- // Only the main cache has a list of subCaches to write
+ // Only the main cache has a list of subCaches and TPRO mappings to write
if ( this->isMainCache() ) {
startOffset += sizeof(dyld_subcache_entry) * this->subCaches.size();
- }
-
- if ( this->needsCacheHeaderImageList() ) {
+ startOffset += sizeof(dyld_cache_tpro_mapping_info) * numTPRORegions(config, this, this->subCaches);
+ }
+
+ if ( this->needsCacheHeaderImageList(options) ) {
startOffset += sizeof(dyld_cache_image_info) * cacheDylibs.size();
startOffset += sizeof(dyld_cache_image_text_info) * cacheDylibs.size();
for ( const CacheDylib& cacheDylib : cacheDylibs ) {
@@ -870,6 +1019,12 @@
// we sort the Chunk's. For now just add placeholder(s) and we'll update the
// size in calculateSlideInfoSize()
+ // TPRO_CONST
+ if ( !this->regions[(uint32_t)Region::Kind::tproConst].chunks.empty() ) {
+ this->tproConstSlideInfo = std::make_unique<SlideInfoChunk>();
+ addLinkeditChunk(this->tproConstSlideInfo.get());
+ }
+
// DATA
if ( !this->regions[(uint32_t)Region::Kind::data].chunks.empty() ) {
this->dataSlideInfo = std::make_unique<SlideInfoChunk>();
@@ -882,6 +1037,12 @@
addLinkeditChunk(this->dataConstSlideInfo.get());
}
+ // TPRO_CONST (on arm64e)
+ if ( !this->regions[(uint32_t)Region::Kind::tproAuthConst].chunks.empty() ) {
+ this->tproAuthConstSlideInfo = std::make_unique<SlideInfoChunk>();
+ addLinkeditChunk(this->tproAuthConstSlideInfo.get());
+ }
+
// AUTH
if ( !this->regions[(uint32_t)Region::Kind::auth].chunks.empty() ) {
this->authSlideInfo = std::make_unique<SlideInfoChunk>();
@@ -904,7 +1065,7 @@
addCodeSignatureChunk(this->codeSignature.get());
}
-void SubCache::addObjCOptsHeaderChunk(ObjCOptimizer& objcOptimizer)
+void SubCache::addObjCOptsHeaderChunk(const BuilderConfig& config, ObjCOptimizer& objcOptimizer)
{
this->objcOptsHeader = std::make_unique<ObjCOptsHeaderChunk>();
this->objcOptsHeader->cacheVMSize = CacheVMSize(objcOptimizer.optsHeaderByteSize);
@@ -912,10 +1073,10 @@
objcOptimizer.optsHeaderChunk = this->objcOptsHeader.get();
- this->addLinkeditChunk(this->objcOptsHeader.get());
-}
-
-void SubCache::addObjCHeaderInfoReadOnlyChunk(ObjCOptimizer& objcOptimizer)
+ this->addReadOnlyChunk(config, this->objcOptsHeader.get());
+}
+
+void SubCache::addObjCHeaderInfoReadOnlyChunk(const BuilderConfig& config, ObjCOptimizer& objcOptimizer)
{
this->objcHeaderInfoRO = std::make_unique<ObjCHeaderInfoReadOnlyChunk>();
this->objcHeaderInfoRO->cacheVMSize = CacheVMSize(objcOptimizer.headerInfoReadOnlyByteSize);
@@ -923,10 +1084,21 @@
objcOptimizer.headerInfoReadOnlyChunk = this->objcHeaderInfoRO.get();
- this->addObjCReadOnlyChunk(this->objcHeaderInfoRO.get());
-}
-
-void SubCache::addObjCSelectorStringsChunk(ObjCSelectorOptimizer& objCSelectorOptimizer)
+ this->addReadOnlyChunk(config, this->objcHeaderInfoRO.get());
+}
+
+void SubCache::addObjCImageInfoChunk(const BuilderConfig& config, ObjCOptimizer& objcOptimizer)
+{
+ this->objcImageInfo = std::make_unique<ObjCImageInfoChunk>();
+ this->objcImageInfo->cacheVMSize = CacheVMSize(objcOptimizer.imageInfoSize);
+ this->objcImageInfo->subCacheFileSize = CacheFileSize(objcOptimizer.imageInfoSize);
+
+ objcOptimizer.imageInfoChunk = this->objcImageInfo.get();
+
+ this->addReadOnlyChunk(config, this->objcImageInfo.get());
+}
+
+void SubCache::addObjCSelectorStringsChunk(const BuilderConfig& config, ObjCSelectorOptimizer& objCSelectorOptimizer)
{
this->objcSelectorStrings = std::make_unique<ObjCStringsChunk>();
this->objcSelectorStrings->cacheVMSize = CacheVMSize(objCSelectorOptimizer.selectorStringsTotalByteSize);
@@ -934,10 +1106,10 @@
objCSelectorOptimizer.selectorStringsChunk = this->objcSelectorStrings.get();
- this->addObjCReadOnlyChunk(this->objcSelectorStrings.get());
-}
-
-void SubCache::addObjCSelectorHashTableChunk(ObjCSelectorOptimizer& objCSelectorOptimizer)
+ this->addReadOnlyChunk(config, this->objcSelectorStrings.get());
+}
+
+void SubCache::addObjCSelectorHashTableChunk(const BuilderConfig& config, ObjCSelectorOptimizer& objCSelectorOptimizer)
{
this->objcSelectorsHashTable = std::make_unique<ObjCSelectorHashTableChunk>();
this->objcSelectorsHashTable->cacheVMSize = CacheVMSize(objCSelectorOptimizer.selectorHashTableTotalByteSize);
@@ -945,10 +1117,10 @@
objCSelectorOptimizer.selectorHashTableChunk = this->objcSelectorsHashTable.get();
- this->addObjCReadOnlyChunk(this->objcSelectorsHashTable.get());
-}
-
-void SubCache::addObjCClassNameStringsChunk(ObjCClassOptimizer& objcClassOptimizer)
+ this->addReadOnlyChunk(config, this->objcSelectorsHashTable.get());
+}
+
+void SubCache::addObjCClassNameStringsChunk(const BuilderConfig& config, ObjCClassOptimizer& objcClassOptimizer)
{
this->objcClassNameStrings = std::make_unique<ObjCStringsChunk>();
this->objcClassNameStrings->cacheVMSize = CacheVMSize(objcClassOptimizer.nameStringsTotalByteSize);
@@ -956,10 +1128,10 @@
objcClassOptimizer.classNameStringsChunk = this->objcClassNameStrings.get();
- this->addObjCReadOnlyChunk(this->objcClassNameStrings.get());
-}
-
-void SubCache::addObjCClassHashTableChunk(ObjCClassOptimizer& objcClassOptimizer)
+ this->addReadOnlyChunk(config, this->objcClassNameStrings.get());
+}
+
+void SubCache::addObjCClassHashTableChunk(const BuilderConfig& config, ObjCClassOptimizer& objcClassOptimizer)
{
this->objcClassesHashTable = std::make_unique<ObjCClassHashTableChunk>();
this->objcClassesHashTable->cacheVMSize = CacheVMSize(objcClassOptimizer.classHashTableTotalByteSize);
@@ -967,10 +1139,10 @@
objcClassOptimizer.classHashTableChunk = this->objcClassesHashTable.get();
- this->addObjCReadOnlyChunk(this->objcClassesHashTable.get());
-}
-
-void SubCache::addObjCProtocolNameStringsChunk(ObjCProtocolOptimizer& objcProtocolOptimizer)
+ this->addReadOnlyChunk(config, this->objcClassesHashTable.get());
+}
+
+void SubCache::addObjCProtocolNameStringsChunk(const BuilderConfig& config, ObjCProtocolOptimizer& objcProtocolOptimizer)
{
this->objcProtocolNameStrings = std::make_unique<ObjCStringsChunk>();
this->objcProtocolNameStrings->cacheVMSize = CacheVMSize(objcProtocolOptimizer.nameStringsTotalByteSize);
@@ -978,10 +1150,10 @@
objcProtocolOptimizer.protocolNameStringsChunk = this->objcProtocolNameStrings.get();
- this->addObjCReadOnlyChunk(this->objcProtocolNameStrings.get());
-}
-
-void SubCache::addObjCProtocolHashTableChunk(ObjCProtocolOptimizer& objcProtocolOptimizer)
+ this->addReadOnlyChunk(config, this->objcProtocolNameStrings.get());
+}
+
+void SubCache::addObjCProtocolHashTableChunk(const BuilderConfig& config, ObjCProtocolOptimizer& objcProtocolOptimizer)
{
this->objcProtocolsHashTable = std::make_unique<ObjCProtocolHashTableChunk>();
this->objcProtocolsHashTable->cacheVMSize = CacheVMSize(objcProtocolOptimizer.protocolHashTableTotalByteSize);
@@ -989,10 +1161,10 @@
objcProtocolOptimizer.protocolHashTableChunk = this->objcProtocolsHashTable.get();
- this->addObjCReadOnlyChunk(this->objcProtocolsHashTable.get());
-}
-
-void SubCache::addObjCProtocolSwiftDemangledNamesChunk(ObjCProtocolOptimizer& objcProtocolOptimizer)
+ this->addReadOnlyChunk(config, this->objcProtocolsHashTable.get());
+}
+
+void SubCache::addObjCProtocolSwiftDemangledNamesChunk(const BuilderConfig& config, ObjCProtocolOptimizer& objcProtocolOptimizer)
{
this->objcSwiftDemangledNameStrings = std::make_unique<ObjCStringsChunk>();
this->objcSwiftDemangledNameStrings->cacheVMSize = CacheVMSize(objcProtocolOptimizer.swiftDemangledNameStringsTotalByteSize);
@@ -1000,10 +1172,23 @@
objcProtocolOptimizer.swiftDemangledNameStringsChunk = this->objcSwiftDemangledNameStrings.get();
- this->addObjCReadOnlyChunk(this->objcSwiftDemangledNameStrings.get());
-}
-
-void SubCache::addObjCIMPCachesChunk(ObjCIMPCachesOptimizer& objcIMPCachesOptimizer)
+ this->addReadOnlyChunk(config, this->objcSwiftDemangledNameStrings.get());
+}
+
+void SubCache::addObjCCanonicalProtocolsChunk(const BuilderConfig& config,
+ ObjCProtocolOptimizer& objcProtocolOptimizer)
+{
+ this->objcCanonicalProtocols = std::make_unique<ObjCCanonicalProtocolsChunk>();
+ this->objcCanonicalProtocols->cacheVMSize = CacheVMSize(objcProtocolOptimizer.canonicalProtocolsTotalByteSize);
+ this->objcCanonicalProtocols->subCacheFileSize = CacheFileSize(objcProtocolOptimizer.canonicalProtocolsTotalByteSize);
+
+ objcProtocolOptimizer.canonicalProtocolsChunk = this->objcCanonicalProtocols.get();
+
+ // Add canonical objc protocols
+ addObjCReadWriteChunk(config, this->objcCanonicalProtocols.get());
+}
+
+void SubCache::addObjCIMPCachesChunk(const BuilderConfig& config, ObjCIMPCachesOptimizer& objcIMPCachesOptimizer)
{
this->objcIMPCaches = std::make_unique<ObjCIMPCachesChunk>();
this->objcIMPCaches->cacheVMSize = CacheVMSize(objcIMPCachesOptimizer.impCachesTotalByteSize);
@@ -1011,20 +1196,20 @@
objcIMPCachesOptimizer.impCachesChunk = this->objcIMPCaches.get();
- this->addLinkeditChunk(this->objcIMPCaches.get());
-}
-
-void SubCache::addObjCCanonicalProtocolsChunk(const BuilderConfig& config,
- ObjCProtocolOptimizer& objcProtocolOptimizer)
-{
- this->objcCanonicalProtocols = std::make_unique<ObjCCanonicalProtocolsChunk>();
- this->objcCanonicalProtocols->cacheVMSize = CacheVMSize(objcProtocolOptimizer.canonicalProtocolsTotalByteSize);
- this->objcCanonicalProtocols->subCacheFileSize = CacheFileSize(objcProtocolOptimizer.canonicalProtocolsTotalByteSize);
-
- objcProtocolOptimizer.canonicalProtocolsChunk = this->objcCanonicalProtocols.get();
-
- // Add canonical objc protocols
- addObjCReadWriteChunk(config, this->objcCanonicalProtocols.get());
+ this->addReadOnlyChunk(config, this->objcIMPCaches.get());
+}
+
+void SubCache::addObjCCategoriesChunk(const BuilderConfig& config,
+ ObjCCategoryOptimizer& objcCategoryOptimizer)
+{
+ this->objcCategories = std::make_unique<ObjCPreAttachedCategoriesChunk>();
+ this->objcCategories->cacheVMSize = CacheVMSize(objcCategoryOptimizer.categoriesTotalByteSize);
+ this->objcCategories->subCacheFileSize = CacheFileSize(objcCategoryOptimizer.categoriesTotalByteSize);
+
+ objcCategoryOptimizer.categoriesChunk = this->objcCategories.get();
+
+ // Add objc categories
+ addReadOnlyChunk(config, this->objcCategories.get());
}
void SubCache::addCacheTrieChunk(DylibTrieOptimizer& dylibTrieOptimizer)
@@ -1040,16 +1225,26 @@
void SubCache::addPatchTableChunk(PatchTableOptimizer& patchTableOptimizer)
{
- // We can't compute the size yet. We need to know how many fixups we have
- // And yet we have an estimate, so we'll use it
+ // We can't compute the size yet so just make an empty chunk
this->patchTable = std::make_unique<PatchTableChunk>();
- this->patchTable->cacheVMSize = CacheVMSize(patchTableOptimizer.patchTableTotalByteSize);
- this->patchTable->subCacheFileSize = CacheFileSize(patchTableOptimizer.patchTableTotalByteSize);
+ this->patchTable->cacheVMSize = CacheVMSize(0ULL);
+ this->patchTable->subCacheFileSize = CacheFileSize(0ULL);
patchTableOptimizer.patchTableChunk = this->patchTable.get();
this->addLinkeditChunk(this->patchTable.get());
+}
+
+void SubCache::addFunctionVariantsChunk(FunctionVariantsOptimizer& optimizer)
+{
+ this->functionVariants = std::make_unique<FunctionVariantsPatchTableChunk>();
+ this->functionVariants->cacheVMSize = CacheVMSize(optimizer.fvInfoTotalByteSize);
+ this->functionVariants->subCacheFileSize = CacheFileSize(optimizer.fvInfoTotalByteSize);
+
+ optimizer.chunk = this->functionVariants.get();
+
+ this->addLinkeditChunk(this->functionVariants.get());
}
void SubCache::addCacheDylibsLoaderChunk(PrebuiltLoaderBuilder& builder)
@@ -1091,7 +1286,21 @@
this->addLinkeditChunk(this->executablesTrie.get());
}
-void SubCache::addSwiftOptsHeaderChunk(SwiftProtocolConformanceOptimizer& opt)
+void SubCache::addPrewarmingDataChunk(const BuilderConfig& config, PrewarmingOptimizer& opt)
+{
+ if ( opt.prewarmingByteSize == 0 )
+ return;
+
+ this->prewarmingChunk = std::make_unique<PrewarmingChunk>(Chunk::Kind::prewarmingData);
+ this->prewarmingChunk->cacheVMSize = CacheVMSize(opt.prewarmingByteSize);
+ this->prewarmingChunk->subCacheFileSize = CacheFileSize(opt.prewarmingByteSize);
+
+ opt.prewarmingChunk = this->prewarmingChunk.get();
+
+ this->addReadOnlyChunk(config, this->prewarmingChunk.get());
+}
+
+void SubCache::addSwiftOptsHeaderChunk(const BuilderConfig& config, SwiftOptimizer& opt)
{
this->swiftOptsHeader = std::make_unique<SwiftOptsHeaderChunk>();
this->swiftOptsHeader->cacheVMSize = CacheVMSize(opt.optsHeaderByteSize);
@@ -1099,10 +1308,10 @@
opt.optsHeaderChunk = this->swiftOptsHeader.get();
- this->addLinkeditChunk(this->swiftOptsHeader.get());
-}
-
-void SubCache::addSwiftTypeHashTableChunk(SwiftProtocolConformanceOptimizer& opt)
+ this->addReadOnlyChunk(config, this->swiftOptsHeader.get());
+}
+
+void SubCache::addSwiftTypeHashTableChunk(const BuilderConfig& config, SwiftOptimizer& opt)
{
this->swiftTypeHashTable = std::make_unique<SwiftProtocolConformancesHashTableChunk>();
this->swiftTypeHashTable->cacheVMSize = CacheVMSize(opt.typeConformancesHashTableSize);
@@ -1110,10 +1319,10 @@
opt.typeConformancesHashTable = this->swiftTypeHashTable.get();
- this->addLinkeditChunk(this->swiftTypeHashTable.get());
-}
-
-void SubCache::addSwiftMetadataHashTableChunk(SwiftProtocolConformanceOptimizer& opt)
+ this->addReadOnlyChunk(config, this->swiftTypeHashTable.get());
+}
+
+void SubCache::addSwiftMetadataHashTableChunk(const BuilderConfig& config, SwiftOptimizer& opt)
{
this->swiftMetadataHashTable = std::make_unique<SwiftProtocolConformancesHashTableChunk>();
this->swiftMetadataHashTable->cacheVMSize = CacheVMSize(opt.metadataConformancesHashTableSize);
@@ -1121,10 +1330,10 @@
opt.metadataConformancesHashTable = this->swiftMetadataHashTable.get();
- this->addLinkeditChunk(this->swiftMetadataHashTable.get());
-}
-
-void SubCache::addSwiftForeignHashTableChunk(SwiftProtocolConformanceOptimizer& opt)
+ this->addReadOnlyChunk(config, this->swiftMetadataHashTable.get());
+}
+
+void SubCache::addSwiftForeignHashTableChunk(const BuilderConfig& config, SwiftOptimizer& opt)
{
this->swiftForeignTypeHashTable = std::make_unique<SwiftProtocolConformancesHashTableChunk>();
this->swiftForeignTypeHashTable->cacheVMSize = CacheVMSize(opt.foreignTypeConformancesHashTableSize);
@@ -1132,7 +1341,19 @@
opt.foreignTypeConformancesHashTable = this->swiftForeignTypeHashTable.get();
- this->addLinkeditChunk(this->swiftForeignTypeHashTable.get());
+ this->addReadOnlyChunk(config, this->swiftForeignTypeHashTable.get());
+}
+
+void SubCache::addSwiftPrespecializedMetadataPointerTableChunks(const BuilderConfig& config, SwiftOptimizer& opt)
+{
+ for ( PointerHashTableOptimizerInfo& tableInfo : opt.prespecializedMetadataHashTables ) {
+ PointerHashTableChunk* chunk = this->pointerHashTables.emplace_back(std::make_unique<PointerHashTableChunk>()).get();
+ chunk->cacheVMSize = CacheVMSize(tableInfo.size);
+ chunk->subCacheFileSize = CacheFileSize(tableInfo.size);
+
+ tableInfo.chunk = chunk;
+ this->addReadOnlyChunk(config, chunk);
+ }
}
void SubCache::addUnmappedSymbols(const BuilderConfig& config, UnmappedSymbolsOptimizer& opt)
@@ -1232,6 +1453,26 @@
case Region::Kind::text:
flags = this->isStubsCache() ? DYLD_CACHE_MAPPING_TEXT_STUBS : 0;
break;
+ case Region::Kind::tproConst:
+ flags = DYLD_CACHE_MAPPING_CONST_DATA | DYLD_CACHE_MAPPING_CONST_TPRO_DATA;
+
+ // Get the slide info
+ if ( this->tproConstSlideInfo ) {
+ slideInfoFileOffset = this->tproConstSlideInfo->subCacheFileOffset;
+ slideInfoFileSize = this->tproConstSlideInfo->usedFileSize;
+ }
+ break;
+ case Region::Kind::tproAuthConst:
+ // Note, we don't set AUTH here. TPRO_CONST doesn't actually contain authenticated fixups, ie, is just data
+ // but we still give it its own Region so that we can place it adjacent to dirty data
+ flags = DYLD_CACHE_MAPPING_CONST_DATA | DYLD_CACHE_MAPPING_CONST_TPRO_DATA;
+
+ // Get the slide info
+ if ( this->tproAuthConstSlideInfo ) {
+ slideInfoFileOffset = this->tproAuthConstSlideInfo->subCacheFileOffset;
+ slideInfoFileSize = this->tproAuthConstSlideInfo->usedFileSize;
+ }
+ break;
case Region::Kind::data:
flags = 0;
@@ -1267,6 +1508,9 @@
slideInfoFileOffset = this->authConstSlideInfo->subCacheFileOffset;
slideInfoFileSize = this->authConstSlideInfo->usedFileSize;
}
+ break;
+ case Region::Kind::readOnly:
+ flags = DYLD_CACHE_READ_ONLY_DATA;
break;
case Region::Kind::linkedit:
flags = 0;
@@ -1305,15 +1549,16 @@
}
void SubCache::writeCacheHeader(const BuilderOptions& options, const BuilderConfig& config,
- const std::span<CacheDylib> cacheDylibs)
+ const std::span<CacheDylib> cacheDylibs,
+ uint32_t osVersion, uint32_t altPlatform, uint32_t altOsVersion)
{
Chunk& cacheHeaderChunk = *this->cacheHeader.get();
dyld_cache_header* dyldCacheHeader = (dyld_cache_header*)cacheHeaderChunk.subCacheBuffer;
// "dyld_v1" + spaces + archName(), with enough spaces to pad to 15 bytes
std::string magic = "dyld_v1";
- magic.append(15 - magic.length() - strlen(options.archs.name()), ' ');
- magic.append(options.archs.name());
+ magic.append(15 - magic.length() - strlen(options.arch.name()), ' ');
+ magic.append(options.arch.name());
assert(magic.length() == 15);
// Num of mappings depends on cache layout.
@@ -1359,12 +1604,14 @@
dyldCacheHeader->progClosuresSize = 0; // no longer used
dyldCacheHeader->progClosuresTrieAddr = 0; // no longer used
dyldCacheHeader->progClosuresTrieSize = 0; // no longer used
- dyldCacheHeader->platform = (uint8_t)options.platform;
+ dyldCacheHeader->platform = options.platform.value();
dyldCacheHeader->formatVersion = 0; //dyld3::closure::kFormatVersion;
dyldCacheHeader->dylibsExpectedOnDisk = !options.dylibsRemovedFromDisk;
- dyldCacheHeader->simulator = options.isSimultor();
+ dyldCacheHeader->simulator = options.isSimulator();
dyldCacheHeader->locallyBuiltCache = options.isLocallyBuiltCache;
dyldCacheHeader->builtFromChainedFixups = false; // no longer used
+ dyldCacheHeader->newFormatTLVs = true;
+ dyldCacheHeader->padding = 0;
dyldCacheHeader->sharedRegionStart = this->subCacheVMAddress.rawValue();
dyldCacheHeader->sharedRegionSize = 0;
dyldCacheHeader->maxSlide = 0; // overwritten later in build if the cache supports ASLR
@@ -1382,9 +1629,9 @@
dyldCacheHeader->programsPBLSetPoolSize = 0; // set later only on the main cache file
dyldCacheHeader->programTrieAddr = 0; // set later only on the main cache file
dyldCacheHeader->programTrieSize = 0; // set later only on the main cache file
- dyldCacheHeader->osVersion = 0; // set later only on the main cache file
- dyldCacheHeader->altPlatform = 0; // set later only on the main cache file
- dyldCacheHeader->altOsVersion = 0; // set later only on the main cache file
+ dyldCacheHeader->osVersion = osVersion;
+ dyldCacheHeader->altPlatform = altPlatform;
+ dyldCacheHeader->altOsVersion = altOsVersion;
dyldCacheHeader->swiftOptsOffset = 0; // set later only on the main cache file
dyldCacheHeader->swiftOptsSize = 0; // set later only on the main cache file
dyldCacheHeader->subCacheArrayOffset = 0;
@@ -1401,25 +1648,30 @@
dyldCacheHeader->cacheAtlasSize = 0; // set later only on the main cache file
dyldCacheHeader->dynamicDataOffset = 0; // set later only on the main cache file
dyldCacheHeader->dynamicDataMaxSize = 0; // set later only on the main cache file
+ dyldCacheHeader->tproMappingsOffset = 0; // set later only on the main cache file
+ dyldCacheHeader->tproMappingsCount = 0; // set later only on the main cache file
+ dyldCacheHeader->prewarmingDataOffset = 0; // set later only on the main cache file
+ dyldCacheHeader->prewarmingDataSize = 0; // set later only on the main cache file
// Fill in old mappings
// And new mappings which also have slide info
this->writeCacheHeaderMappings();
- this->addCacheHeaderImageInfo(options, cacheDylibs);
+ this->addCacheHeaderImageInfo(options, config, cacheDylibs);
}
void SubCache::addMainCacheHeaderInfo(const BuilderOptions& options, const BuilderConfig& config,
const std::span<CacheDylib> cacheDylibs,
CacheVMSize totalVMSize, uint64_t maxSlide,
- uint32_t osVersion, uint32_t altPlatform, uint32_t altOsVersion,
CacheVMAddress dyldInCacheUnslidAddr,
CacheVMAddress dyldInCacheEntryUnslidAddr,
const DylibTrieOptimizer& dylibTrieOptimizer,
const ObjCOptimizer& objcOptimizer,
- const SwiftProtocolConformanceOptimizer& swiftProtocolConformanceOpt,
+ const SwiftOptimizer& swiftOpt,
const PatchTableOptimizer& patchTableOptimizer,
- const PrebuiltLoaderBuilder& prebuiltLoaderBuilder)
+ const FunctionVariantsOptimizer& functionVariantOptimizer,
+ const PrebuiltLoaderBuilder& prebuiltLoaderBuilder,
+ const PrewarmingOptimizer& prewarmingOptimizer)
{
const CacheVMAddress cacheBaseAddress = config.layout.cacheBaseAddress;
@@ -1432,15 +1684,15 @@
dyldCacheHeader->dylibsTrieAddr = dylibTrieOptimizer.dylibsTrieChunk->cacheVMAddress.rawValue();
dyldCacheHeader->dylibsTrieSize = dylibTrieOptimizer.dylibsTrieChunk->subCacheFileSize.rawValue();
- if ( !objcOptimizer.objcDylibs.empty() ) {
+ // Disable objc optimizations from EK shared cache
+ bool emitObjcOpts = !options.platform.isExclaveKit();
+ if ( !objcOptimizer.objcDylibs.empty() && emitObjcOpts ) {
dyldCacheHeader->objcOptsOffset = (objcOptimizer.optsHeaderChunk->cacheVMAddress - cacheBaseAddress).rawValue();
dyldCacheHeader->objcOptsSize = objcOptimizer.optsHeaderChunk->subCacheFileSize.rawValue();
- }
-
- if ( !objcOptimizer.objcDylibs.empty() ) {
- const auto& opt = swiftProtocolConformanceOpt;
+
+ const auto& opt = swiftOpt;
dyldCacheHeader->swiftOptsOffset = (opt.optsHeaderChunk->cacheVMAddress - cacheBaseAddress).rawValue();
- dyldCacheHeader->objcOptsSize = opt.optsHeaderChunk->subCacheFileSize.rawValue();
+ dyldCacheHeader->swiftOptsSize = opt.optsHeaderChunk->subCacheFileSize.rawValue();
}
dyldCacheHeader->patchInfoAddr = patchTableOptimizer.patchTableChunk->cacheVMAddress.rawValue();
@@ -1455,16 +1707,15 @@
dyldCacheHeader->dyldInCacheMH = dyldInCacheUnslidAddr.rawValue();
dyldCacheHeader->dyldInCacheEntry = dyldInCacheEntryUnslidAddr.rawValue();
- dyldCacheHeader->osVersion = osVersion;
- dyldCacheHeader->altPlatform = altPlatform;
- dyldCacheHeader->altOsVersion = altOsVersion;
-
// record max slide now that final size is established
dyldCacheHeader->maxSlide = maxSlide;
// TODO: Build the atlas
dyldCacheHeader->cacheAtlasOffset = 0; // set later only on the main cache file
dyldCacheHeader->cacheAtlasSize = 0; // set later only on the main cache file
+
+ dyldCacheHeader->functionVariantInfoAddr = functionVariantOptimizer.chunk->cacheVMAddress.rawValue();
+ dyldCacheHeader->functionVariantInfoSize = functionVariantOptimizer.fvInfoTotalByteSize;
// The main cache has offsets to all the caches
if ( !this->subCaches.empty() ) {
@@ -1485,6 +1736,30 @@
dyldCacheHeader->dynamicDataOffset = (dynamicConfig->cacheVMAddress - cacheBaseAddress).rawValue();
dyldCacheHeader->dynamicDataMaxSize = dynamicConfig->cacheVMSize.rawValue();
}
+
+ // Find all the TPRO regions
+ if ( dyldCacheHeader->tproMappingsCount != 0 ) {
+ assert(cacheHeaderChunk.subCacheFileOffset.rawValue() == 0);
+ auto* mappings = (dyld_cache_tpro_mapping_info*)((uint8_t*)dyldCacheHeader + dyldCacheHeader->tproMappingsOffset);
+ __block uint32_t index = 0;
+ forEachTPRORegion(config, this, this->subCaches, ^(CacheVMAddress firstChunkAddress, CacheVMAddress lastChunkAddress, CacheVMSize lastChunkSize) {
+ assert(index < dyldCacheHeader->tproMappingsCount);
+
+ CacheVMAddress vmAddr = firstChunkAddress;
+ CacheVMSize vmSize = (lastChunkAddress - firstChunkAddress) + lastChunkSize;
+
+ dyld_cache_tpro_mapping_info* mapping = & mappings[index];
+ mapping->unslidAddress = vmAddr.rawValue();
+ mapping->size = vmSize.rawValue();
+
+ ++index;
+ });
+ }
+
+ if ( prewarmingOptimizer.prewarmingChunk != nullptr ) {
+ dyldCacheHeader->prewarmingDataOffset = (prewarmingOptimizer.prewarmingChunk->cacheVMAddress - cacheBaseAddress).rawValue();
+ dyldCacheHeader->prewarmingDataSize = prewarmingOptimizer.prewarmingChunk->subCacheFileSize.rawValue();
+ }
}
void SubCache::addSymbolsCacheHeaderInfo(const UnmappedSymbolsOptimizer& optimizer)
@@ -1508,9 +1783,10 @@
}
void SubCache::addCacheHeaderImageInfo(const BuilderOptions& options,
+ const BuilderConfig& config,
const std::span<CacheDylib> cacheDylibs)
{
- if ( !this->needsCacheHeaderImageList() )
+ if ( !this->needsCacheHeaderImageList(options) )
return;
Chunk& cacheHeaderChunk = *this->cacheHeader.get();
@@ -1525,13 +1801,18 @@
dyldCacheHeader->subCacheArrayOffset = (uint32_t)(dyldCacheHeader->imagesTextOffset + sizeof(dyld_cache_image_text_info) * cacheDylibs.size());
dyldCacheHeader->subCacheArrayCount = (uint32_t)this->subCaches.size();
+ // Always set the TPRO offset, but the count will only be set in the main cache
+ dyldCacheHeader->tproMappingsOffset = (uint32_t)(dyldCacheHeader->subCacheArrayOffset + sizeof(dyld_subcache_entry) * dyldCacheHeader->subCacheArrayCount);
+ if ( this->isMainCache() )
+ dyldCacheHeader->tproMappingsCount = numTPRORegions(config, this, this->subCaches);
+
// calculate start of text image array and trailing string pool
auto* textImages = (dyld_cache_image_text_info*)((uint8_t*)dyldCacheHeader + dyldCacheHeader->imagesTextOffset);
- uint32_t stringOffset = (uint32_t)(dyldCacheHeader->subCacheArrayOffset + sizeof(dyld_subcache_entry) * dyldCacheHeader->subCacheArrayCount);
+ uint32_t stringOffset = (uint32_t)(dyldCacheHeader->tproMappingsOffset + sizeof(dyld_cache_tpro_mapping_info) * dyldCacheHeader->tproMappingsCount);
// write text image array and image names pool at same time
for ( const CacheDylib& cacheDylib : cacheDylibs ) {
- cacheDylib.inputMF->getUuid(textImages->uuid);
+ cacheDylib.inputHdr->getUuid(textImages->uuid);
textImages->loadAddress = cacheDylib.cacheLoadAddress.rawValue();
textImages->textSegmentSize = (uint32_t)cacheDylib.segments.front().cacheVMSize.rawValue();
textImages->pathOffset = stringOffset;
@@ -1639,16 +1920,17 @@
return this->kind == Kind::stubsCustomer;
}
-bool SubCache::needsCacheHeaderImageList() const
+bool SubCache::needsCacheHeaderImageList(const BuilderOptions& options) const
{
// Symbols and stubs files don't need an image list
- // We'd like to not add the image list to subcaches, only the main cache, but Rosetta needs
- // the image list on subCaches.
switch ( this->kind ) {
case Kind::mainDevelopment:
case Kind::mainCustomer:
+ return true;
case Kind::subUniversal:
- return true;
+ // We'd like to not add the image list to subcaches, only the main cache, but Rosetta needs
+ // the image list on subCaches.
+ return options.arch.sameCpu(mach_o::Architecture::x86_64);
case Kind::stubsDevelopment:
case Kind::stubsCustomer:
case Kind::symbols:
@@ -1771,6 +2053,9 @@
__block MachOFile::ChainedFixupPointerOnDisk* lastFixup = nullptr;
__block uint64_t lastPageIndex = ~0ULL;
for ( Chunk* chunk : region.chunks ) {
+ if ( chunk->isAlignChunk() )
+ continue;
+
SlidChunk* slidChunk = chunk->isSlidChunk();
slidChunk->tracker.forEachFixup(^(void *loc, bool& stop) {
@@ -1947,6 +2232,110 @@
return Error();
}
+Error SubCache::computeSlideInfoV5(const BuilderConfig& config,
+ cache_builder::SlideInfoChunk* slideChunk,
+ Region& region)
+{
+ __block Diagnostics diag;
+
+ bool canContainAuthPointers = region.canContainAuthPointers();
+
+ assert((region.subCacheVMSize.rawValue() % config.slideInfo.slideInfoPageSize) == 0);
+ dyld_cache_slide_info5* info = (dyld_cache_slide_info5*)slideChunk->subCacheBuffer;
+ info->version = 5;
+ info->page_size = config.slideInfo.slideInfoPageSize;
+ info->page_starts_count = (uint32_t)region.subCacheVMSize.rawValue() / config.slideInfo.slideInfoPageSize;
+ info->value_add = config.layout.cacheBaseAddress.rawValue();
+
+ assert((sizeof(dyld_cache_slide_info5) + (info->page_starts_count * sizeof(uint16_t))) <= slideChunk->cacheVMSize.rawValue());
+
+ std::fill(&info->page_starts[0], &info->page_starts[info->page_starts_count], DYLD_CACHE_SLIDE_V5_PAGE_ATTR_NO_REBASE);
+
+ // Walk each fixup in each segment. Every time we cross a page, add a page start
+ __block dyld_cache_slide_pointer5* lastFixup = nullptr;
+ __block uint64_t lastPageIndex = ~0ULL;
+ for ( Chunk* chunk : region.chunks ) {
+ SlidChunk* slidChunk = chunk->isSlidChunk();
+
+ slidChunk->tracker.forEachFixup(^(void *loc, bool& stop) {
+ // V5 fixups must be 8-byte aligned
+ assert(((uint64_t)loc % 8) == 0);
+
+ VMOffset vmOffsetInSegment((uint64_t)loc - (uint64_t)slidChunk->subCacheBuffer);
+ CacheVMAddress fixupVMAddr = slidChunk->cacheVMAddress + vmOffsetInSegment;
+ uint64_t pageIndex = (fixupVMAddr - region.subCacheVMAddress).rawValue() / info->page_size;
+
+ // If we are on a new page, then start a new chain
+ if ( pageIndex != lastPageIndex ) {
+ uint64_t vmOffsetInPage = fixupVMAddr.rawValue() % info->page_size;
+ info->page_starts[pageIndex] = vmOffsetInPage;
+ }
+ else {
+ // Patch the previous fixup on this page to point to this one
+ lastFixup->auth.next = ((uint64_t)loc - (uint64_t)lastFixup) / 8;
+ }
+
+ dyld_cache_slide_pointer5* fixupLocation = (dyld_cache_slide_pointer5*)loc;
+
+ // Convert this fixup from the chained format in the cache builder, to the version we want in the cache file
+ CacheVMAddress vmAddr = Fixup::Cache64::getCacheVMAddressFromLocation(config.layout.cacheBaseAddress,
+ loc);
+
+ uint8_t high8 = Fixup::Cache64::getHigh8(loc);
+ VMOffset cacheVMOffset = vmAddr - config.layout.cacheBaseAddress;
+
+ uint16_t authDiversity = 0;
+ bool authIsAddr = false;
+ uint8_t authKey = 0;
+ if ( Fixup::Cache64::hasAuthData(loc, authDiversity, authIsAddr, authKey) ) {
+ // Authenticated value
+ assert(high8 == 0);
+ assert(canContainAuthPointers);
+ assert((authKey == 0) || (authKey == 2)); // IA (0) or DA (2)
+
+ fixupLocation->auth.runtimeOffset = cacheVMOffset.rawValue();
+ fixupLocation->auth.diversity = authDiversity;
+ fixupLocation->auth.addrDiv = authIsAddr ? 1 : 0;
+ fixupLocation->auth.keyIsData = (authKey == 2) ? 1 : 0;
+ fixupLocation->auth.next = 0;
+ fixupLocation->auth.auth = 1;
+
+ if ( fixupLocation->auth.runtimeOffset != cacheVMOffset.rawValue() ) {
+ diag.error("cache offset 0x%llx exceeds v5 format", cacheVMOffset.rawValue());
+ stop = true;
+ return;
+ }
+ } else {
+ // Unauthenticated value
+ fixupLocation->regular.runtimeOffset = cacheVMOffset.rawValue();
+ fixupLocation->regular.high8 = high8;
+ fixupLocation->regular.next = 0;
+ fixupLocation->regular.unused = 0;
+ fixupLocation->regular.auth = 0;
+
+ if ( fixupLocation->regular.runtimeOffset != cacheVMOffset.rawValue() ) {
+ diag.error("cache offset 0x%llx exceeds v5 format", cacheVMOffset.rawValue());
+ stop = true;
+ return;
+ }
+ }
+
+ lastFixup = fixupLocation;
+ lastPageIndex = pageIndex;
+ });
+ if ( diag.hasError() )
+ break;
+ }
+
+ if ( diag.hasError() )
+ return Error("could not build slide info because: %s", diag.errorMessageCStr());
+
+ // V5 doesn't deduplicate content like V1, so the used size is the original size too
+ slideChunk->usedFileSize = slideChunk->subCacheFileSize;
+
+ return Error();
+}
+
Error SubCache::computeSlideInfoForRegion(const BuilderConfig& config,
cache_builder::SlideInfoChunk* slideChunk,
Region& region)
@@ -1958,6 +2347,8 @@
return computeSlideInfoV2(config, slideChunk, region);
case cache_builder::SlideInfo::SlideInfoFormat::v3:
return computeSlideInfoV3(config, slideChunk, region);
+ case cache_builder::SlideInfo::SlideInfoFormat::v5:
+ return computeSlideInfoV5(config, slideChunk, region);
}
return Error();
@@ -1968,8 +2359,10 @@
Diagnostics diag;
for ( Chunk* chunk : region.chunks ) {
+ if ( chunk->isAlignChunk() )
+ continue;
+
SlidChunk* slidChunk = chunk->isSlidChunk();
-
slidChunk->tracker.forEachFixup(^(void *loc, bool& stop) {
if ( config.layout.is64 ) {
CacheVMAddress vmAddr = Fixup::Cache64::getCacheVMAddressFromLocation(config.layout.cacheBaseAddress, loc);
@@ -1996,6 +2389,12 @@
case Region::Kind::text:
// No slide info for text
break;
+ case Region::Kind::tproConst:
+ if ( config.slideInfo.slideInfoFormat.has_value() )
+ err = computeSlideInfoForRegion(config, this->tproConstSlideInfo.get(), region);
+ else
+ err = convertChainsToVMAddresses(config, region);
+ break;
case Region::Kind::data:
if ( config.slideInfo.slideInfoFormat.has_value() )
err = computeSlideInfoForRegion(config, this->dataSlideInfo.get(), region);
@@ -2008,6 +2407,12 @@
else
err = convertChainsToVMAddresses(config, region);
break;
+ case Region::Kind::tproAuthConst:
+ if ( config.slideInfo.slideInfoFormat.has_value() )
+ err = computeSlideInfoForRegion(config, this->tproAuthConstSlideInfo.get(), region);
+ else
+ err = convertChainsToVMAddresses(config, region);
+ break;
case Region::Kind::auth:
if ( config.slideInfo.slideInfoFormat.has_value() )
err = computeSlideInfoForRegion(config, this->authSlideInfo.get(), region);
@@ -2020,8 +2425,9 @@
else
err = convertChainsToVMAddresses(config, region);
break;
+ case Region::Kind::readOnly:
case Region::Kind::linkedit:
- // No slide info for text
+ // No slide info for linkedit
break;
case Region::Kind::unmapped:
case Region::Kind::dynamicConfig: