Loading...
--- dyld/dyld-960/common/DyldSharedCache.cpp
+++ dyld/dyld-1165.3/common/DyldSharedCache.cpp
@@ -22,6 +22,9 @@
* @APPLE_LICENSE_HEADER_END@
*/
+#include <TargetConditionals.h>
+
+#if !TARGET_OS_EXCLAVEKIT
#include <dirent.h>
#include <sys/errno.h>
@@ -42,7 +45,7 @@
#include <vector>
#include <unordered_map>
#include <unordered_set>
-#include "SharedCacheBuilder.h"
+// #include "SharedCacheBuilder.h"
#include "FileUtils.h"
#endif
@@ -61,14 +64,18 @@
#include <sstream>
#endif
+using dyld3::MachOFile;
+using dyld3::MachOLoaded;
+using dyld3::MachOAnalyzer;
using dyld4::PrebuiltLoader;
using dyld4::PrebuiltLoaderSet;
+
#if (BUILDING_LIBDYLD || BUILDING_DYLD)
VIS_HIDDEN bool gEnableSharedCacheDataConst = false;
#endif
-#if BUILDING_CACHE_BUILDER
+#if 0 // BUILDING_CACHE_BUILDER
DyldSharedCache::CreateResults DyldSharedCache::create(const CreateOptions& options,
const dyld3::closure::FileSystem& fileSystem,
const std::vector<MappedMachO>& dylibsToCache,
@@ -230,10 +237,17 @@
return (const T)(addr + slide);
}
-uint64_t DyldSharedCache::getCodeSignAddress() const
-{
- auto mappings = (const dyld_cache_mapping_info*)((uint8_t*)this + header.mappingOffset);
- return mappings[header.mappingCount-1].address + mappings[header.mappingCount-1].size;
+const char* DyldSharedCache::getCacheTypeName(uint64_t cacheType) {
+ switch ( cacheType ) {
+ case kDyldSharedCacheTypeDevelopment:
+ return "development";
+ case kDyldSharedCacheTypeProduction:
+ return "production";
+ case kDyldSharedCacheTypeUniversal:
+ return "universal";
+ default:
+ return "unknown";
+ }
}
void DyldSharedCache::forEachRegion(void (^handler)(const void* content, uint64_t vmAddr, uint64_t size,
@@ -268,6 +282,40 @@
}
}
+const char* DyldSharedCache::mappingName(uint32_t maxProt, uint64_t flags)
+{
+ const char* mappingName = "";
+ if ( maxProt & VM_PROT_EXECUTE ) {
+ if ( flags & DYLD_CACHE_MAPPING_TEXT_STUBS ) {
+ mappingName = "__TEXT_STUBS";
+ } else {
+ mappingName = "__TEXT";
+ }
+ } else if ( maxProt & VM_PROT_WRITE ) {
+ if ( flags & DYLD_CACHE_MAPPING_AUTH_DATA ) {
+ if ( flags & DYLD_CACHE_MAPPING_DIRTY_DATA )
+ mappingName = "__AUTH_DIRTY";
+ else if ( flags & DYLD_CACHE_MAPPING_CONST_DATA )
+ mappingName = "__AUTH_CONST";
+ else
+ mappingName = "__AUTH";
+ } else {
+ if ( flags & DYLD_CACHE_MAPPING_DIRTY_DATA )
+ mappingName = "__DATA_DIRTY";
+ else if ( flags & DYLD_CACHE_MAPPING_CONST_DATA )
+ mappingName = "__DATA_CONST";
+ else
+ mappingName = "__DATA";
+ }
+ }
+ else if ( maxProt & VM_PROT_READ ) {
+ mappingName = "__LINKEDIT";
+ } else {
+ mappingName = "*unknown*";
+ }
+ return mappingName;
+}
+
void DyldSharedCache::forEachRange(void (^handler)(const char* mappingName,
uint64_t unslidVMAddr, uint64_t vmSize,
uint32_t cacheFileIndex, uint64_t fileOffset,
@@ -279,31 +327,7 @@
forEachCache(^(const DyldSharedCache *cache, bool& stopCache) {
cache->forEachRegion(^(const void *content, uint64_t unslidVMAddr, uint64_t size,
uint32_t initProt, uint32_t maxProt, uint64_t flags, bool& stopRegion) {
- const char* mappingName = "";
- if ( maxProt & VM_PROT_EXECUTE ) {
- mappingName = "__TEXT";
- } else if ( maxProt & VM_PROT_WRITE ) {
- if ( flags & DYLD_CACHE_MAPPING_AUTH_DATA ) {
- if ( flags & DYLD_CACHE_MAPPING_DIRTY_DATA )
- mappingName = "__AUTH_DIRTY";
- else if ( flags & DYLD_CACHE_MAPPING_CONST_DATA )
- mappingName = "__AUTH_CONST";
- else
- mappingName = "__AUTH";
- } else {
- if ( flags & DYLD_CACHE_MAPPING_DIRTY_DATA )
- mappingName = "__DATA_DIRTY";
- else if ( flags & DYLD_CACHE_MAPPING_CONST_DATA )
- mappingName = "__DATA_CONST";
- else
- mappingName = "__DATA";
- }
- }
- else if ( maxProt & VM_PROT_READ ) {
- mappingName = "__LINKEDIT";
- } else {
- mappingName = "*unknown*";
- }
+ const char* mappingName = DyldSharedCache::mappingName(maxProt, flags);
uint64_t fileOffset = (uint8_t*)content - (uint8_t*)cache;
bool stop = false;
handler(mappingName, unslidVMAddr, size, cacheFileIndex, fileOffset, initProt, maxProt, stop);
@@ -336,9 +360,8 @@
if ( header.mappingOffset <= __offsetof(dyld_cache_header, subCacheArrayCount) )
return;
- const dyld_subcache_entry* subCaches = (const dyld_subcache_entry*)((uint8_t*)this + header.subCacheArrayOffset);
for (uint32_t i = 0; i != header.subCacheArrayCount; ++i) {
- const DyldSharedCache* cache = (const DyldSharedCache*)((uint8_t*)this + subCaches[i].cacheVMOffset);
+ const DyldSharedCache* cache = (const DyldSharedCache*)((uintptr_t)this + this->getSubCacheVmOffset(i));
handler(cache, stop);
if ( stop )
return;
@@ -353,7 +376,44 @@
return header.subCacheArrayCount;
}
-bool DyldSharedCache::inCache(const void* addr, size_t length, bool& readOnly) const
+int32_t DyldSharedCache::getSubCacheIndex(const void* addr) const
+{
+ __block int32_t index = 0;
+ __block bool found = false;
+ this->forEachCache(^(const DyldSharedCache *cache, bool &stopCache) {
+ bool readOnly = false;
+ if ( cache->inCache(addr, sizeof(uint64_t), readOnly) ) {
+ stopCache = true;
+ found = true;
+ return;
+ }
+ index++;
+ });
+ int32_t result = found ? index : -1;
+ return result;
+}
+
+void DyldSharedCache::getSubCacheUuid(uint8_t index, uint8_t uuid[]) const {
+ if (header.mappingOffset <= __offsetof(dyld_cache_header, cacheSubType) ) {
+ const dyld_subcache_entry_v1* subCacheEntries = (dyld_subcache_entry_v1*)((uintptr_t)this + header.subCacheArrayOffset);
+ memcpy(uuid, subCacheEntries[index].uuid, 16);
+ } else {
+ const dyld_subcache_entry* subCacheEntries = (dyld_subcache_entry*)((uintptr_t)this + header.subCacheArrayOffset);
+ memcpy(uuid, subCacheEntries[index].uuid, 16);
+ }
+}
+
+uint64_t DyldSharedCache::getSubCacheVmOffset(uint8_t index) const {
+ if (header.mappingOffset <= __offsetof(dyld_cache_header, cacheSubType) ) {
+ const dyld_subcache_entry_v1* subCacheEntries = (dyld_subcache_entry_v1*)((uintptr_t)this + header.subCacheArrayOffset);
+ return subCacheEntries[index].cacheVMOffset;
+ } else {
+ const dyld_subcache_entry* subCacheEntries = (dyld_subcache_entry*)((uintptr_t)this + header.subCacheArrayOffset);
+ return subCacheEntries[index].cacheVMOffset;
+ }
+}
+
+bool DyldSharedCache::inCache(const void* addr, size_t length, bool& immutable) const
{
// quick out if before start of cache
if ( addr < this )
@@ -363,22 +423,19 @@
uintptr_t slide = (uintptr_t)this - (uintptr_t)(mappings[0].address);
uintptr_t unslidStart = (uintptr_t)addr - slide;
- // quick out if after end of cache
- const dyld_cache_mapping_info* lastMapping = &mappings[header.mappingCount - 1];
- if ( unslidStart > (lastMapping->address + lastMapping->size) )
- return false;
-
- // walk cache regions
- const dyld_cache_mapping_info* mappingsEnd = &mappings[header.mappingCount];
- uintptr_t unslidEnd = unslidStart + length;
- for (const dyld_cache_mapping_info* m=mappings; m < mappingsEnd; ++m) {
- if ( (unslidStart >= m->address) && (unslidEnd < (m->address+m->size)) ) {
- readOnly = ((m->initProt & VM_PROT_WRITE) == 0);
- return true;
- }
- }
-
- return false;
+ // walk cache ranges
+ __block bool found = false;
+ auto inRange = ^(const char* mappingName, uint64_t unslidVMAddr, uint64_t vmSize, uint32_t cacheFileIndex,
+ uint64_t fileOffset, uint32_t initProt, uint32_t maxProt, bool& stopRange) {
+ if ( (unslidVMAddr <= unslidStart) && ((unslidStart+length) < (unslidVMAddr+vmSize)) ) {
+ found = true;
+ immutable = ((maxProt & VM_PROT_WRITE) == 0);
+ stopRange = true;
+ }
+ };
+ this->forEachRange(inRange, nullptr);
+
+ return found;
}
bool DyldSharedCache::isAlias(const char* path) const {
@@ -491,7 +548,7 @@
// check for cache without local symbols info
if (!this->hasLocalSymbolsInfo())
return nullptr;
- const auto localInfo = (dyld_cache_local_symbols_info*)((uint8_t*)this + header.localSymbolsOffset);
+ const auto localInfo = (dyld_cache_local_symbols_info*)((uintptr_t)this + header.localSymbolsOffset);
return getLocalNlistEntries(localInfo);
}
@@ -500,7 +557,7 @@
// check for cache without local symbols info
if (!this->hasLocalSymbolsInfo())
return 0;
- const auto localInfo = (dyld_cache_local_symbols_info*)((uint8_t*)this + header.localSymbolsOffset);
+ const auto localInfo = (dyld_cache_local_symbols_info*)((uintptr_t)this + header.localSymbolsOffset);
return localInfo->nlistCount;
}
@@ -514,7 +571,7 @@
// check for cache without local symbols info
if (!this->hasLocalSymbolsInfo())
return nullptr;
- const auto localInfo = (dyld_cache_local_symbols_info*)((uint8_t*)this + header.localSymbolsOffset);
+ const auto localInfo = (dyld_cache_local_symbols_info*)((uintptr_t)this + header.localSymbolsOffset);
return getLocalStrings(localInfo);
}
@@ -523,7 +580,7 @@
// check for cache without local symbols info
if (!this->hasLocalSymbolsInfo())
return 0;
- const auto localInfo = (dyld_cache_local_symbols_info*)((uint8_t*)this + header.localSymbolsOffset);
+ const auto localInfo = (dyld_cache_local_symbols_info*)((uintptr_t)this + header.localSymbolsOffset);
return localInfo->stringsSize;
}
@@ -533,7 +590,7 @@
const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
uint32_t dyldCacheImageIndex;
if ( hasImagePath(dylibPath, dyldCacheImageIndex) )
- return (dyld3::MachOFile*)((uint8_t*)this + dylibs[dyldCacheImageIndex].address - mappings[0].address);
+ return (dyld3::MachOFile*)((uintptr_t)this + dylibs[dyldCacheImageIndex].address - mappings[0].address);
return nullptr;
}
@@ -542,7 +599,7 @@
// check for cache without local symbols info
if (!this->hasLocalSymbolsInfo())
return;
- const auto localInfo = (dyld_cache_local_symbols_info*)((uint8_t*)this + header.localSymbolsOffset);
+ const auto localInfo = (dyld_cache_local_symbols_info*)((uintptr_t)this + header.localSymbolsOffset);
if ( header.mappingOffset >= __offsetof(dyld_cache_header, symbolFileUUID) ) {
// On new caches, the dylibOffset is 64-bits, and is a VM offset
@@ -567,10 +624,17 @@
const mach_header* DyldSharedCache::getIndexedImageEntry(uint32_t index, uint64_t& mTime, uint64_t& inode) const
{
const dyld_cache_image_info* dylibs = images();
- const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
+ const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((uintptr_t)this + header.mappingOffset);
mTime = dylibs[index].modTime;
inode = dylibs[index].inode;
- return (mach_header*)((uint8_t*)this + dylibs[index].address - mappings[0].address);
+ return (mach_header*)((uintptr_t)this + dylibs[index].address - mappings[0].address);
+}
+
+const mach_header* DyldSharedCache::getIndexedImageEntry(uint32_t index) const
+{
+ uint64_t mTime = 0;
+ uint64_t inode = 0;
+ return this->getIndexedImageEntry(index, mTime, inode);
}
@@ -646,9 +710,9 @@
else if ( maxProt == VM_PROT_READ )
prot = "RO";
if ( size > 1024*1024 )
- sprintf(lineBuffer, "mapping %s %4lluMB 0x%0llX -> 0x%0llX\n", prot, size/(1024*1024), vmAddr, vmAddr+size);
+ snprintf(lineBuffer, sizeof(lineBuffer), "mapping %s %4lluMB 0x%0llX -> 0x%0llX\n", prot, size/(1024*1024), vmAddr, vmAddr+size);
else
- sprintf(lineBuffer, "mapping %s %4lluKB 0x%0llX -> 0x%0llX\n", prot, size/1024, vmAddr, vmAddr+size);
+ snprintf(lineBuffer, sizeof(lineBuffer), "mapping %s %4lluKB 0x%0llX -> 0x%0llX\n", prot, size/1024, vmAddr, vmAddr+size);
result += lineBuffer;
});
@@ -660,7 +724,7 @@
const dyld3::MachOFile* mf = (dyld3::MachOFile*)mh;
mf->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& info, bool& stop) {
char lineBuffer[256];
- sprintf(lineBuffer, "\t%16s 0x%08llX -> 0x%08llX\n", info.segName, info.vmAddr, info.vmAddr+info.vmSize);
+ snprintf(lineBuffer, sizeof(lineBuffer), "\t%16s 0x%08llX -> 0x%08llX\n", info.segName, info.vmAddr, info.vmAddr+info.vmSize);
result += lineBuffer;
});
result += "\n";
@@ -758,33 +822,26 @@
return false;
}
-bool DyldSharedCache::isOverridablePath(const char* dylibPath) const
-{
- // all dylibs in customer dyld cache cannot be overridden except libdispatch.dylib
- if ( header.cacheType == kDyldSharedCacheTypeProduction ) {
- return (strcmp(dylibPath, "/usr/lib/system/libdispatch.dylib") == 0);
- }
- // in dev caches we can override all paths
- return true;
-}
-
-bool DyldSharedCache::hasNonOverridablePath(const char* dylibPath) const
-{
- // all dylibs in customer dyld cache cannot be overridden except libdispatch.dylib
- bool pathIsInDyldCacheWhichCannotBeOverridden = false;
- if ( header.cacheType == kDyldSharedCacheTypeProduction ) {
- uint32_t imageIndex;
- pathIsInDyldCacheWhichCannotBeOverridden = this->hasImagePath(dylibPath, imageIndex);
- if ( pathIsInDyldCacheWhichCannotBeOverridden && isOverridablePath(dylibPath) )
- pathIsInDyldCacheWhichCannotBeOverridden = false;
- }
- return pathIsInDyldCacheWhichCannotBeOverridden;
-}
-
intptr_t DyldSharedCache::slide() const
{
const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
return (intptr_t)this - (intptr_t)(mappings[0].address);
+}
+
+const PrebuiltLoaderSet* DyldSharedCache::dylibsLoaderSet() const
+{
+ if ( header.mappingOffset < __offsetof(dyld_cache_header, programTrieSize) )
+ return nullptr;
+ const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
+ if ( mappings[0].fileOffset != 0 )
+ return nullptr;
+ if ( header.mappingOffset < __offsetof(dyld_cache_header, dylibsPBLSetAddr) )
+ return nullptr;
+ if ( header.dylibsPBLSetAddr == 0 )
+ return nullptr;
+ uintptr_t slide = (uintptr_t)this - (uintptr_t)(mappings[0].address);
+ const PrebuiltLoaderSet* pbLoaderSet = (PrebuiltLoaderSet*)(this->header.dylibsPBLSetAddr + slide);
+ return pbLoaderSet;
}
const PrebuiltLoader* DyldSharedCache::findPrebuiltLoader(const char* path) const
@@ -794,16 +851,10 @@
uint32_t imageIndex;
if ( !this->hasImagePath(path, imageIndex) )
return nullptr;
- const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
- if ( mappings[0].fileOffset != 0 )
- return nullptr;
- if ( header.mappingOffset < __offsetof(dyld_cache_header, dylibsPBLSetAddr) )
- return nullptr;
- if ( header.dylibsPBLSetAddr == 0 )
- return nullptr;
- uintptr_t slide = (uintptr_t)this - (uintptr_t)(mappings[0].address);
- const PrebuiltLoaderSet* pbLoaderSet = (PrebuiltLoaderSet*)(this->header.dylibsPBLSetAddr + slide);
- return pbLoaderSet->atIndex(imageIndex);
+ if ( const PrebuiltLoaderSet* pbLoaderSet = this->dylibsLoaderSet() )
+ return pbLoaderSet->atIndex(imageIndex);
+
+ return nullptr;
}
void DyldSharedCache::forEachLaunchLoaderSet(void (^handler)(const char* executableRuntimePath, const PrebuiltLoaderSet* pbls)) const
@@ -853,17 +904,22 @@
bool DyldSharedCache::hasLaunchLoaderSetWithCDHash(const char* cdHashString) const
{
+ return (findLaunchLoaderSetWithCDHash(cdHashString) != nullptr);
+}
+
+const dyld4::PrebuiltLoaderSet* DyldSharedCache::findLaunchLoaderSetWithCDHash(const char* cdHashString) const
+{
if ( cdHashString == nullptr )
- return false;
+ return nullptr;
// Check source doesn't overflow buffer. strncat unfortunately isn't available
if ( strlen(cdHashString) >= 128 )
- return false;
-
- char buffer[128] = { '\0' };
- strcat(buffer, "/cdhash/");
- strlcat(buffer, cdHashString, sizeof(buffer));
- return findLaunchLoaderSet(buffer) != nullptr;
+ return nullptr;
+
+ char cdPath[140];
+ strlcpy(cdPath, "/cdhash/", sizeof(cdPath));
+ strlcat(cdPath, cdHashString, sizeof(cdPath));
+ return findLaunchLoaderSet(cdPath);
}
@@ -1001,6 +1057,11 @@
}
}
+const void* DyldSharedCache::patchTable() const
+{
+ return getAddrField<const void*>(header.patchInfoAddr);
+}
+
uint32_t DyldSharedCache::patchInfoVersion() const {
if ( header.mappingOffset <= __offsetof(dyld_cache_header, swiftOptsSize) ) {
return 1;
@@ -1026,25 +1087,13 @@
return patchArray[imageIndex].patchExportsCount;
}
- // V2 and newer structs
- if ( patchVersion == 2 ) {
- const dyld_cache_patch_info_v2* patchInfo = getAddrField<dyld_cache_patch_info_v2*>(header.patchInfoAddr);
- const dyld_cache_image_patches_v2* patchArray = getAddrField<dyld_cache_image_patches_v2*>(patchInfo->patchTableArrayAddr);
-
- // FIXME: This shouldn't be possible. The cache builder always adds 1 dyld_cache_image_patches_v2 per-image. But for now
- // be conservative to match V1.
- if (imageIndex > patchInfo->patchTableArrayCount)
- return 0;
-
- const dyld_cache_image_patches_v2& imagePatches = patchArray[imageIndex];
- return imagePatches.patchExportsCount;
- }
-
- // Unknown version
- assert(false);
-}
-
-void DyldSharedCache::forEachPatchableExport(uint32_t imageIndex, void (^handler)(uint32_t dylibVMOffsetOfImpl, const char* exportName)) const {
+ // V2/V3 and newer structs
+ PatchTable patchTable(this->patchTable(), header.patchInfoAddr);
+ return patchTable.patchableExportCount(imageIndex);
+}
+
+void DyldSharedCache::forEachPatchableExport(uint32_t imageIndex, void (^handler)(uint32_t dylibVMOffsetOfImpl, const char* exportName,
+ PatchKind patchKind)) const {
if ( header.patchInfoAddr == 0 )
return;
@@ -1078,43 +1127,22 @@
// Convert from a cache offset to an offset from the input image
uint32_t imageOffset = (uint32_t)((cacheUnslidAddress + patchExport.cacheOffsetOfImpl) - imageLoadAddress);
- handler(imageOffset, exportName);
- }
-
- return;
- }
-
- // V2 and newer structs
- if ( patchVersion == 2 ) {
- const auto* patchInfo = getAddrField<dyld_cache_patch_info_v2*>(header.patchInfoAddr);
- const auto* patchArray = getAddrField<dyld_cache_image_patches_v2*>(patchInfo->patchTableArrayAddr);
- const auto* imageExports = getAddrField<dyld_cache_image_export_v2*>(patchInfo->patchImageExportsArrayAddr);
- const char* exportNames = getAddrField<char*>(patchInfo->patchExportNamesAddr);
-
- // FIXME: This shouldn't be possible. The cache builder always adds 1 dyld_cache_image_patches_v2 per-image. But for now
- // be conservative to match V1.
- if (imageIndex > patchInfo->patchTableArrayCount)
- return;
-
- const dyld_cache_image_patches_v2& imagePatches = patchArray[imageIndex];
- // FIXME: This shouldn't be possible. We should catch this in the builder instead
- if ( (imagePatches.patchExportsStartIndex + imagePatches.patchExportsCount) > patchInfo->patchImageExportsArrayCount )
- return;
-
- for (uint64_t exportIndex = 0; exportIndex != imagePatches.patchExportsCount; ++exportIndex) {
- const dyld_cache_image_export_v2& imageExport = imageExports[imagePatches.patchExportsStartIndex + exportIndex];
- const char* exportName = ( imageExport.exportNameOffset < patchInfo->patchExportNamesSize ) ? &exportNames[imageExport.exportNameOffset] : "";
- handler(imageExport.dylibOffsetOfImpl, exportName);
- }
- return;
- }
-
- assert(false);
-}
-
+ handler(imageOffset, exportName, PatchKind::regular);
+ }
+
+ return;
+ }
+
+ // V2 newer structs
+ PatchTable patchTable(this->patchTable(), header.patchInfoAddr);
+ patchTable.forEachPatchableExport(imageIndex, handler);
+}
+
+#if BUILDING_SHARED_CACHE_UTIL
void DyldSharedCache::forEachPatchableUseOfExport(uint32_t imageIndex, uint32_t dylibVMOffsetOfImpl,
void (^handler)(uint32_t userImageIndex, uint32_t userVMOffset,
- MachOLoaded::PointerMetaData pmd, uint64_t addend)) const {
+ MachOLoaded::PointerMetaData pmd, uint64_t addend,
+ bool isWeakImport)) const {
if ( header.patchInfoAddr == 0 )
return;
@@ -1220,72 +1248,17 @@
pmd.key = patchLocation.key;
pmd.usesAddrDiversity = patchLocation.usesAddressDiversity;
- handler(userImageIndex, userVMOffset, pmd, patchLocation.getAddend());
+ handler(userImageIndex, userVMOffset, pmd, patchLocation.getAddend(), false);
}
}
return;
}
- // V2 and newer structs
- if ( patchVersion == 2 ) {
- const auto* patchInfo = getAddrField<dyld_cache_patch_info_v2*>(header.patchInfoAddr);
- const auto* patchArray = getAddrField<dyld_cache_image_patches_v2*>(patchInfo->patchTableArrayAddr);
- const auto* imageExports = getAddrField<dyld_cache_image_export_v2*>(patchInfo->patchImageExportsArrayAddr);
- const auto* clientArray = getAddrField<dyld_cache_image_clients_v2*>(patchInfo->patchClientsArrayAddr);
- const auto* clientExports = getAddrField<dyld_cache_patchable_export_v2*>(patchInfo->patchClientExportsArrayAddr);
- const auto* patchLocations = getAddrField<dyld_cache_patchable_location_v2*>(patchInfo->patchLocationArrayAddr);
-
- // FIXME: This shouldn't be possible. The cache builder always adds 1 dyld_cache_image_patches_v2 per-image. But for now
- // be conservative to match V1.
- if (imageIndex > patchInfo->patchTableArrayCount)
- return;
-
- const dyld_cache_image_patches_v2& imagePatches = patchArray[imageIndex];
- // FIXME: This shouldn't be possible. We should catch this in the builder instead
- if ( (imagePatches.patchClientsStartIndex + imagePatches.patchClientsCount) > patchInfo->patchClientsArrayCount )
- return;
-
- for (uint64_t clientIndex = 0; clientIndex != imagePatches.patchClientsCount; ++clientIndex) {
- const dyld_cache_image_clients_v2& client = clientArray[imagePatches.patchClientsStartIndex + clientIndex];
-
- // FIXME: This shouldn't be possible. We should catch this in the builder instead
- if ( (client.patchExportsStartIndex + client.patchExportsCount) > patchInfo->patchClientExportsArrayCount )
- return;
-
- for (uint64_t exportIndex = 0; exportIndex != client.patchExportsCount; ++exportIndex) {
- const dyld_cache_patchable_export_v2& clientExport = clientExports[client.patchExportsStartIndex + exportIndex];
-
- // The client export points to an image export from the image we are rooting
- if ( clientExport.imageExportIndex > patchInfo->patchImageExportsArrayCount )
- return;
-
- const dyld_cache_image_export_v2& imageExport = imageExports[clientExport.imageExportIndex];
-
- if ( imageExport.dylibOffsetOfImpl != dylibVMOffsetOfImpl )
- continue;
-
- if ( (clientExport.patchLocationsStartIndex + clientExport.patchLocationsCount) > patchInfo->patchLocationArrayCount )
- return;
-
- for (uint64_t locationIndex = 0; locationIndex != clientExport.patchLocationsCount; ++locationIndex) {
- const dyld_cache_patchable_location_v2& patchLocation = patchLocations[clientExport.patchLocationsStartIndex + locationIndex];
-
- dyld3::MachOLoaded::PointerMetaData pmd;
- pmd.diversity = patchLocation.discriminator;
- pmd.high8 = patchLocation.high7 << 1;
- pmd.authenticated = patchLocation.authenticated;
- pmd.key = patchLocation.key;
- pmd.usesAddrDiversity = patchLocation.usesAddressDiversity;
-
- handler(client.clientDylibIndex, patchLocation.dylibOffsetOfUse, pmd, patchLocation.getAddend());
- }
- }
- }
- return;
- }
-
- assert(false);
-}
+ // V2/V3 and newer structs
+ PatchTable patchTable(this->patchTable(), header.patchInfoAddr);
+ patchTable.forEachPatchableUseOfExport(imageIndex, dylibVMOffsetOfImpl, handler);
+}
+#endif
bool DyldSharedCache::shouldPatchClientOfImage(uint32_t imageIndex, uint32_t userImageIndex) const {
if ( header.patchInfoAddr == 0 )
@@ -1299,37 +1272,14 @@
return false;
}
- // V2 and newer structs
- if ( patchVersion == 2 ) {
- const auto* patchInfo = getAddrField<dyld_cache_patch_info_v2*>(header.patchInfoAddr);
- const auto* patchArray = getAddrField<dyld_cache_image_patches_v2*>(patchInfo->patchTableArrayAddr);
- const auto* clientArray = getAddrField<dyld_cache_image_clients_v2*>(patchInfo->patchClientsArrayAddr);
-
- // FIXME: This shouldn't be possible. The cache builder always adds 1 dyld_cache_image_patches_v2 per-image. But for now
- // be conservative to match V1.
- if (imageIndex > patchInfo->patchTableArrayCount)
- return false;
-
- const dyld_cache_image_patches_v2& imagePatches = patchArray[imageIndex];
- // FIXME: This shouldn't be possible. We should catch this in the builder instead
- if ( (imagePatches.patchClientsStartIndex + imagePatches.patchClientsCount) > patchInfo->patchClientsArrayCount )
- return false;
-
- for (uint64_t clientIndex = 0; clientIndex != imagePatches.patchClientsCount; ++clientIndex) {
- const dyld_cache_image_clients_v2& client = clientArray[imagePatches.patchClientsStartIndex + clientIndex];
-
- // We only want fixups in a specific image. Skip any others
- if ( client.clientDylibIndex == userImageIndex )
- return true;
- }
- return false;
- }
-
- assert(false);
+ // V2/V3 and newer structs
+ PatchTable patchTable(this->patchTable(), header.patchInfoAddr);
+ return patchTable.imageHasClient(imageIndex, userImageIndex);
}
void DyldSharedCache::forEachPatchableUseOfExportInImage(uint32_t imageIndex, uint32_t dylibVMOffsetOfImpl, uint32_t userImageIndex,
- void (^handler)(uint32_t userVMOffset, MachOLoaded::PointerMetaData pmd, uint64_t addend)) const {
+ void (^handler)(uint32_t userVMOffset, MachOLoaded::PointerMetaData pmd, uint64_t addend,
+ bool isWeakImport)) const {
if ( header.patchInfoAddr == 0 )
return;
@@ -1437,88 +1387,30 @@
pmd.key = patchLocation.key;
pmd.usesAddrDiversity = patchLocation.usesAddressDiversity;
- handler(userVMOffset, pmd, patchLocation.getAddend());
+ handler(userVMOffset, pmd, patchLocation.getAddend(), false);
}
}
}
return;
}
- // V2 and newer structs
- if ( patchVersion == 2 ) {
- const auto* patchInfo = getAddrField<dyld_cache_patch_info_v2*>(header.patchInfoAddr);
- const auto* patchArray = getAddrField<dyld_cache_image_patches_v2*>(patchInfo->patchTableArrayAddr);
- const auto* imageExports = getAddrField<dyld_cache_image_export_v2*>(patchInfo->patchImageExportsArrayAddr);
- const auto* clientArray = getAddrField<dyld_cache_image_clients_v2*>(patchInfo->patchClientsArrayAddr);
- const auto* clientExports = getAddrField<dyld_cache_patchable_export_v2*>(patchInfo->patchClientExportsArrayAddr);
- const auto* patchLocations = getAddrField<dyld_cache_patchable_location_v2*>(patchInfo->patchLocationArrayAddr);
-
- // FIXME: This shouldn't be possible. The cache builder always adds 1 dyld_cache_image_patches_v2 per-image. But for now
- // be conservative to match V1.
- if (imageIndex > patchInfo->patchTableArrayCount)
- return;
-
- const dyld_cache_image_patches_v2& imagePatches = patchArray[imageIndex];
- // FIXME: This shouldn't be possible. We should catch this in the builder instead
- if ( (imagePatches.patchClientsStartIndex + imagePatches.patchClientsCount) > patchInfo->patchClientsArrayCount )
- return;
-
- for (uint64_t clientIndex = 0; clientIndex != imagePatches.patchClientsCount; ++clientIndex) {
- const dyld_cache_image_clients_v2& client = clientArray[imagePatches.patchClientsStartIndex + clientIndex];
-
- // We only want fixups in a specific image. Skip any others
- if ( client.clientDylibIndex != userImageIndex )
- continue;
-
- // FIXME: This shouldn't be possible. We should catch this in the builder instead
- if ( (client.patchExportsStartIndex + client.patchExportsCount) > patchInfo->patchClientExportsArrayCount )
- return;
-
- for (uint64_t exportIndex = 0; exportIndex != client.patchExportsCount; ++exportIndex) {
- const dyld_cache_patchable_export_v2& clientExport = clientExports[client.patchExportsStartIndex + exportIndex];
-
- // The client export points to an image export from the image we are rooting
- if ( clientExport.imageExportIndex > patchInfo->patchImageExportsArrayCount )
- return;
-
- const dyld_cache_image_export_v2& imageExport = imageExports[clientExport.imageExportIndex];
-
- if ( imageExport.dylibOffsetOfImpl != dylibVMOffsetOfImpl )
- continue;
-
- if ( (clientExport.patchLocationsStartIndex + clientExport.patchLocationsCount) > patchInfo->patchLocationArrayCount )
- return;
-
- for (uint64_t locationIndex = 0; locationIndex != clientExport.patchLocationsCount; ++locationIndex) {
- const dyld_cache_patchable_location_v2& patchLocation = patchLocations[clientExport.patchLocationsStartIndex + locationIndex];
-
- dyld3::MachOLoaded::PointerMetaData pmd;
- pmd.diversity = patchLocation.discriminator;
- pmd.high8 = patchLocation.high7 << 1;
- pmd.authenticated = patchLocation.authenticated;
- pmd.key = patchLocation.key;
- pmd.usesAddrDiversity = patchLocation.usesAddressDiversity;
-
- handler(patchLocation.dylibOffsetOfUse, pmd, patchLocation.getAddend());
- }
- }
-
- // We only wanted to process this image. We are now done
- break;
- }
- return;
- }
-
- assert(false);
+ // V2/V3 and newer structs
+ PatchTable patchTable(this->patchTable(), header.patchInfoAddr);
+ patchTable.forEachPatchableUseOfExportInImage(imageIndex, dylibVMOffsetOfImpl,
+ userImageIndex, handler);
}
void DyldSharedCache::forEachPatchableUseOfExport(uint32_t imageIndex, uint32_t dylibVMOffsetOfImpl,
void (^handler)(uint64_t cacheVMOffset,
- MachOLoaded::PointerMetaData pmd, uint64_t addend)) const {
+ MachOLoaded::PointerMetaData pmd, uint64_t addend,
+ bool isWeakImport)) const {
if ( header.patchInfoAddr == 0 )
return;
uint32_t patchVersion = patchInfoVersion();
+
+ // Get GOT patches if we have them
+ this->forEachPatchableGOTUseOfExport(imageIndex, dylibVMOffsetOfImpl, handler);
if ( patchVersion == 1 ) {
// Old cache. The patch table uses the V1 structs
@@ -1559,95 +1451,53 @@
pmd.key = patchLocation.key;
pmd.usesAddrDiversity = patchLocation.usesAddressDiversity;
- handler(patchLocation.cacheOffset, pmd, patchLocation.getAddend());
+ handler(patchLocation.cacheOffset, pmd, patchLocation.getAddend(), false);
}
}
return;
}
- // V2 and newer structs
- if ( patchVersion == 2 ) {
- const auto* patchInfo = getAddrField<dyld_cache_patch_info_v2*>(header.patchInfoAddr);
- const auto* patchArray = getAddrField<dyld_cache_image_patches_v2*>(patchInfo->patchTableArrayAddr);
- const auto* imageExports = getAddrField<dyld_cache_image_export_v2*>(patchInfo->patchImageExportsArrayAddr);
- const auto* clientArray = getAddrField<dyld_cache_image_clients_v2*>(patchInfo->patchClientsArrayAddr);
- const auto* clientExports = getAddrField<dyld_cache_patchable_export_v2*>(patchInfo->patchClientExportsArrayAddr);
- const auto* patchLocations = getAddrField<dyld_cache_patchable_location_v2*>(patchInfo->patchLocationArrayAddr);
-
- // FIXME: This shouldn't be possible. The cache builder always adds 1 dyld_cache_image_patches_v2 per-image. But for now
- // be conservative to match V1.
- if (imageIndex > patchInfo->patchTableArrayCount)
- return;
-
- const dyld_cache_image_patches_v2& imagePatches = patchArray[imageIndex];
- // FIXME: This shouldn't be possible. We should catch this in the builder instead
- if ( (imagePatches.patchClientsStartIndex + imagePatches.patchClientsCount) > patchInfo->patchClientsArrayCount )
- return;
-
- uint64_t cacheUnslidAddress = unslidLoadAddress();
-
- for (uint64_t clientIndex = 0; clientIndex != imagePatches.patchClientsCount; ++clientIndex) {
- const dyld_cache_image_clients_v2& client = clientArray[imagePatches.patchClientsStartIndex + clientIndex];
-
- // FIXME: This shouldn't be possible. We should catch this in the builder instead
- if ( (client.patchExportsStartIndex + client.patchExportsCount) > patchInfo->patchClientExportsArrayCount )
- return;
-
- // The callback wants a cache offset, so convert from "image + address" to a cache offset
- uint64_t clientModTime;
- uint64_t clientINode;
- const dyld3::MachOAnalyzer* clientMA = (dyld3::MachOAnalyzer*)(this->getIndexedImageEntry(client.clientDylibIndex, clientModTime, clientINode));
- if ( clientMA == nullptr )
- return;
-
- uint64_t clientUnslidAddress = clientMA->preferredLoadAddress();
-
- for (uint64_t exportIndex = 0; exportIndex != client.patchExportsCount; ++exportIndex) {
- const dyld_cache_patchable_export_v2& clientExport = clientExports[client.patchExportsStartIndex + exportIndex];
-
- // The client export points to an image export from the image we are rooting
- if ( clientExport.imageExportIndex > patchInfo->patchImageExportsArrayCount )
- return;
-
- const dyld_cache_image_export_v2& imageExport = imageExports[clientExport.imageExportIndex];
-
- if ( imageExport.dylibOffsetOfImpl != dylibVMOffsetOfImpl )
- continue;
-
- if ( (clientExport.patchLocationsStartIndex + clientExport.patchLocationsCount) > patchInfo->patchLocationArrayCount )
- return;
-
- for (uint64_t locationIndex = 0; locationIndex != clientExport.patchLocationsCount; ++locationIndex) {
- const dyld_cache_patchable_location_v2& patchLocation = patchLocations[clientExport.patchLocationsStartIndex + locationIndex];
-
- dyld3::MachOLoaded::PointerMetaData pmd;
- pmd.diversity = patchLocation.discriminator;
- pmd.high8 = patchLocation.high7 << 1;
- pmd.authenticated = patchLocation.authenticated;
- pmd.key = patchLocation.key;
- pmd.usesAddrDiversity = patchLocation.usesAddressDiversity;
-
- uint64_t cacheOffset = (clientUnslidAddress + patchLocation.dylibOffsetOfUse) - cacheUnslidAddress;
- handler(cacheOffset, pmd, patchLocation.getAddend());
- }
- }
- }
- return;
- }
-
- assert(false);
+ // V2/V3 and newer structs
+ auto getDylibAddress = ^(uint32_t dylibImageIndex) {
+ auto* clientMF = (dyld3::MachOFile*)(this->getIndexedImageEntry(dylibImageIndex));
+ if ( clientMF == nullptr )
+ return 0ULL;
+ return clientMF->preferredLoadAddress();
+ };
+ PatchTable patchTable(this->patchTable(), header.patchInfoAddr);
+ patchTable.forEachPatchableCacheUseOfExport(imageIndex, dylibVMOffsetOfImpl,
+ this->unslidLoadAddress(),
+ getDylibAddress, handler);
+}
+
+void DyldSharedCache::forEachPatchableGOTUseOfExport(uint32_t imageIndex, uint32_t dylibVMOffsetOfImpl,
+ void (^handler)(uint64_t cacheVMOffset,
+ MachOFile::PointerMetaData pmd,
+ uint64_t addend,
+ bool isWeakImport)) const {
+ if ( header.patchInfoAddr == 0 )
+ return;
+
+ uint32_t patchVersion = patchInfoVersion();
+
+ if ( patchVersion == 1 ) {
+ // Old cache. Only V3 has GOT patching
+ return;
+ }
+
+ // V3 and newer structs
+ PatchTable patchTable(this->patchTable(), header.patchInfoAddr);
+ patchTable.forEachPatchableGOTUseOfExport(imageIndex, dylibVMOffsetOfImpl, handler);
}
#if !(BUILDING_LIBDYLD || BUILDING_DYLD)
// MRM map file generator
-std::string DyldSharedCache::generateJSONMap(const char* disposition) const {
+std::string DyldSharedCache::generateJSONMap(const char* disposition, uuid_t cache_uuid, bool verbose) const {
dyld3::json::Node cacheNode;
cacheNode.map["version"].value = "1";
cacheNode.map["disposition"].value = disposition;
cacheNode.map["base-address"].value = dyld3::json::hex(unslidLoadAddress());
- uuid_t cache_uuid;
- getUUID(cache_uuid);
uuid_string_t cache_uuidStr;
uuid_unparse(cache_uuid, cache_uuidStr);
cacheNode.map["uuid"].value = cache_uuidStr;
@@ -1670,6 +1520,21 @@
segmentNode.map["name"].value = info.segName;
segmentNode.map["start-vmaddr"].value = dyld3::json::hex(info.vmAddr);
segmentNode.map["end-vmaddr"].value = dyld3::json::hex(info.vmAddr + info.vmSize);
+
+ // Add sections in verbose mode
+ if ( verbose ) {
+ __block dyld3::json::Node sectionsNode;
+ ma->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo& sectInfo, bool malformedSectionRange, bool& stopSection) {
+ if ( sectInfo.segInfo.segIndex == info.segIndex ) {
+ dyld3::json::Node sectionNode;
+ sectionNode.map["name"].value = sectInfo.sectName;
+ sectionNode.map["size"] = dyld3::json::Node(sectInfo.sectSize);
+ sectionsNode.array.push_back(sectionNode);
+ }
+ });
+ if ( !sectionsNode.array.empty() )
+ segmentNode.map["sections"] = std::move(sectionsNode);
+ }
segmentsNode.array.push_back(segmentNode);
});
imageNode.map["segments"] = segmentsNode;
@@ -1721,8 +1586,10 @@
// until we find one with slide info
forEachCache(^(const DyldSharedCache *cache, bool& stopCache) {
cache->forEachSlideInfo(^(uint64_t mappingStartAddress, uint64_t mappingSize, const uint8_t *mappingPagesStart, uint64_t slideInfoOffset, uint64_t slideInfoSize, const dyld_cache_slide_info *slideInfoHeader) {
- assert(slideInfoHeader->version >= 2);
- if ( slideInfoHeader->version == 2 ) {
+ if ( slideInfoHeader->version == 1 ) {
+ pointerFormat = VMAddrConverter::SharedCacheFormat::v1;
+ pointerValueAdd = 0;
+ } else if ( slideInfoHeader->version == 2 ) {
const dyld_cache_slide_info2* slideInfo = (dyld_cache_slide_info2*)(slideInfoHeader);
assert(slideInfo->delta_mask == 0x00FFFF0000000000);
pointerFormat = VMAddrConverter::SharedCacheFormat::v2_x86_64_tbi;
@@ -1756,13 +1623,19 @@
#endif
bool DyldSharedCache::inDyldCache(const DyldSharedCache* cache, const dyld3::MachOFile* mf) {
- // FIXME: The cache PBLS has a null state.config.dyldCache.addr when built, so we can't do the
- // range check. We might be able to remove the #if below if we can instead set the cache in the config.
-#if BUILDING_CACHE_BUILDER
- return mf->inDyldCache();
-#else
return mf->inDyldCache() && (cache != nullptr) && ((uintptr_t)mf >= (uintptr_t)cache) && ((uintptr_t)mf < ((uintptr_t)cache + cache->mappedSize()));
-#endif
+}
+
+bool DyldSharedCache::isSubCachePath(const char* leafName)
+{
+ const char* firstDot = strchr(leafName, '.');
+ // check for files with a suffix, to know wether or not they are sub-caches
+ if ( firstDot != NULL ) {
+ // skip files that are not of the format "<baseName>.development", as they are sub-caches
+ if ( strcmp(firstDot, ".development") != 0 )
+ return true;
+ }
+ return false;
}
#if !(BUILDING_LIBDYLD || BUILDING_DYLD)
@@ -1771,6 +1644,8 @@
uint64_t baseCacheUnslidAddress,
uint8_t* buffer)
{
+ // We don't need to map R-X as we aren't running the code here, so only allow mapping up to RW
+ const uint32_t maxPermissions = VM_PROT_READ | VM_PROT_WRITE;
struct stat statbuf;
if ( ::stat(path, &statbuf) ) {
fprintf(stderr, "Error: stat failed for dyld shared cache at %s\n", path);
@@ -1789,6 +1664,11 @@
return nullptr;
}
const dyld_cache_header* header = (dyld_cache_header*)firstPage;
+ if ( strncmp(header->magic, "dyld_v1", 7) != 0 ) {
+ fprintf(stderr, "Error: Expected cache file magic to be 'dyld_v1...' in %s\n", path);
+ return nullptr;
+ }
+
if ( header->mappingCount == 0 ) {
fprintf(stderr, "Error: No mapping in shared cache file at %s\n", path);
return nullptr;
@@ -1819,7 +1699,7 @@
for (uint32_t i=0; i < header->mappingCount; ++i) {
uint64_t mappingAddressOffset = mappings[i].address - mappings[0].address;
void* mapped_cache = ::mmap((void*)(buffer + mappingAddressOffset + subCacheBufferOffset), (size_t)mappings[i].size,
- mappings[i].maxProt, MAP_FIXED | MAP_PRIVATE, cache_fd, mappings[i].fileOffset);
+ mappings[i].maxProt & maxPermissions, MAP_FIXED | MAP_PRIVATE, cache_fd, mappings[i].fileOffset);
if (mapped_cache == MAP_FAILED) {
fprintf(stderr, "Error: mmap() for shared cache at %s failed, errno=%d\n", path, errno);
return nullptr;
@@ -1839,20 +1719,33 @@
std::vector<const DyldSharedCache*> caches;
caches.push_back(cache);
+ std::string basePath = std::string(path);
+ if ( cache->header.cacheType == kDyldSharedCacheTypeUniversal )
+ {
+ std::size_t pos = basePath.find(DYLD_SHARED_CACHE_DEVELOPMENT_EXT);
+ if (pos != std::string::npos)
+ basePath = basePath.substr(0, basePath.size() - 12);
+ }
// Load all subcaches, if we have them
if ( cache->header.mappingOffset >= __offsetof(dyld_cache_header, subCacheArrayCount) ) {
if ( cache->header.subCacheArrayCount != 0 ) {
const dyld_subcache_entry* subCacheEntries = (dyld_subcache_entry*)((uint8_t*)cache + cache->header.subCacheArrayOffset);
+ bool hasCacheSuffix = cache->header.mappingOffset > __offsetof(dyld_cache_header, cacheSubType);
for (uint32_t i = 0; i != cache->header.subCacheArrayCount; ++i) {
- std::string subCachePath = std::string(path) + "." + dyld3::json::decimal(i + 1);
+ std::string subCachePath = std::string(path) + "." + dyld3::json::unpaddedDecimal(i + 1);
+ if ( hasCacheSuffix ) {
+ subCachePath = basePath + subCacheEntries[i].fileSuffix;
+ }
const DyldSharedCache* subCache = DyldSharedCache::mapCacheFile(subCachePath.c_str(), cache->unslidLoadAddress(), (uint8_t*)cache);
if ( subCache == nullptr )
return {};
- if ( memcmp(subCache->header.uuid, subCacheEntries[i].uuid, 16) != 0 ) {
+ uint8_t uuid[16];
+ cache->getSubCacheUuid(i, uuid);
+ if ( memcmp(subCache->header.uuid, uuid, 16) != 0 ) {
uuid_string_t expectedUUIDString;
- uuid_unparse_upper(subCacheEntries[i].uuid, expectedUUIDString);
+ uuid_unparse_upper(uuid, expectedUUIDString);
uuid_string_t foundUUIDString;
uuid_unparse_upper(subCache->header.uuid, foundUUIDString);
fprintf(stderr, "Error: SubCache[%i] UUID mismatch. Expected %s, got %s\n", i, expectedUUIDString, foundUUIDString);
@@ -1966,7 +1859,21 @@
return (uint8_t*)(legacyCacheDataRegionMapping()->address) + slide;
}
-const objc_opt::objc_opt_t* DyldSharedCache::objcOpt() const
+const ObjCOptimizationHeader* DyldSharedCache::objcOpts() const
+{
+ if ( header.mappingOffset <= __offsetof(dyld_cache_header, objcOptsSize) )
+ return nullptr;
+
+ return (const ObjCOptimizationHeader*)((char*)this + header.objcOptsOffset);
+}
+
+#if BUILDING_CACHE_BUILDER
+const objc_opt::objc_opt_t* DyldSharedCache::oldObjcOpt() const
+{
+ return nullptr;
+}
+#else
+const objc_opt::objc_opt_t* DyldSharedCache::oldObjcOpt() const
{
// Find the objc image
__block const dyld3::MachOAnalyzer* objcMA = nullptr;
@@ -1975,15 +1882,6 @@
uint64_t mTime;
uint64_t inode;
objcMA = (dyld3::MachOAnalyzer*)(this->getIndexedImageEntry(imageIndex, mTime, inode));
- } else {
-#if BUILDING_CACHE_BUILDER
- // The cache builder might call this before prior to the trie being written.
- // In this case, we can still use the image list which is always available
- forEachImage(^(const mach_header *mh, const char *installName) {
- if ( !strcmp(installName, "/usr/lib/libobjc.A.dylib") )
- objcMA = (const dyld3::MachOAnalyzer*)mh;
- });
-#endif
}
if ( objcMA == nullptr )
@@ -2012,6 +1910,7 @@
return nullptr;
}
+#endif
const void* DyldSharedCache::objcOptPtrs() const
{
@@ -2050,31 +1949,109 @@
return objcPointersContent;
}
+bool DyldSharedCache::hasOptimizedObjC() const
+{
+ return (this->objcOpts() != nullptr) || (this->oldObjcOpt() != nullptr);
+}
+
+uint32_t DyldSharedCache::objcOptVersion() const
+{
+ if ( const ObjCOptimizationHeader* opts = this->objcOpts() )
+ return opts->version;
+ if ( const objc_opt::objc_opt_t* opts = this->oldObjcOpt() )
+ return opts->version;
+ return 0;
+}
+
+uint32_t DyldSharedCache::objcOptFlags() const
+{
+ if ( const ObjCOptimizationHeader* opts = this->objcOpts() )
+ return 0;
+ if ( const objc_opt::objc_opt_t* opts = this->oldObjcOpt() )
+ return opts->flags;
+ return 0;
+}
+
+const objc::HeaderInfoRO* DyldSharedCache::objcHeaderInfoRO() const
+{
+ if ( const ObjCOptimizationHeader* opts = this->objcOpts() ) {
+ if ( opts->headerInfoROCacheOffset != 0 )
+ return (const objc::HeaderInfoRO*)((char*)this + opts->headerInfoROCacheOffset);
+ return nullptr;
+ }
+ if ( const objc_opt::objc_opt_t* opts = this->oldObjcOpt() )
+ return (const objc::HeaderInfoRO*)opts->headeropt_ro();
+ return nullptr;
+}
+
+const objc::HeaderInfoRW* DyldSharedCache::objcHeaderInfoRW() const
+{
+ if ( const ObjCOptimizationHeader* opts = this->objcOpts() ) {
+ if ( opts->headerInfoRWCacheOffset != 0 )
+ return (const objc::HeaderInfoRW*)((char*)this + opts->headerInfoRWCacheOffset);
+ return nullptr;
+ }
+ if ( const objc_opt::objc_opt_t* opts = this->oldObjcOpt() )
+ return (const objc::HeaderInfoRW*)opts->headeropt_rw();
+ return nullptr;
+}
+
+const objc::SelectorHashTable* DyldSharedCache::objcSelectorHashTable() const
+{
+ if ( const ObjCOptimizationHeader* opts = this->objcOpts() ) {
+ if ( opts->selectorHashTableCacheOffset != 0 )
+ return (const objc::SelectorHashTable*)((char*)this + opts->selectorHashTableCacheOffset);
+ return nullptr;
+ }
+ if ( const objc_opt::objc_opt_t* opts = this->oldObjcOpt() )
+ return opts->selectorOpt();
+ return nullptr;
+}
+
+const objc::ClassHashTable* DyldSharedCache::objcClassHashTable() const
+{
+ if ( const ObjCOptimizationHeader* opts = this->objcOpts() ) {
+ if ( opts->classHashTableCacheOffset != 0 )
+ return (const objc::ClassHashTable*)((char*)this + opts->classHashTableCacheOffset);
+ return nullptr;
+ }
+ if ( const objc_opt::objc_opt_t* opts = this->oldObjcOpt() )
+ return opts->classOpt();
+ return nullptr;
+}
+
+const objc::ProtocolHashTable* DyldSharedCache::objcProtocolHashTable() const
+{
+ if ( const ObjCOptimizationHeader* opts = this->objcOpts() ) {
+ if ( opts->protocolHashTableCacheOffset != 0 )
+ return (const objc::ProtocolHashTable*)((char*)this + opts->protocolHashTableCacheOffset);
+ return nullptr;
+ }
+ if ( const objc_opt::objc_opt_t* opts = this->oldObjcOpt() )
+ return opts->protocolOpt();
+ return nullptr;
+}
+
+const void* DyldSharedCache::objcRelativeMethodListsBaseAddress() const
+{
+ if ( const ObjCOptimizationHeader* opts = this->objcOpts() ) {
+ if ( opts->relativeMethodSelectorBaseAddressOffset != 0 )
+ return (const void*)((char*)this + opts->relativeMethodSelectorBaseAddressOffset);
+ return nullptr;
+ }
+ if ( const objc_opt::objc_opt_t* opts = this->oldObjcOpt() )
+ return opts->relativeMethodListsBaseAddress();
+ return nullptr;
+}
+
#if !(BUILDING_LIBDYLD || BUILDING_DYLD)
uint64_t DyldSharedCache::sharedCacheRelativeSelectorBaseVMAddress() const {
- if ( header.mappingOffset <= __offsetof(dyld_cache_header, symbolFileUUID) )
+ const void* value = this->objcRelativeMethodListsBaseAddress();
+ if ( !value )
return 0;
- // In newer shared caches, relative method list selectors are offsets from the magic selector in libobjc
- __block uint64_t sharedCacheRelativeSelectorBaseVMAddress = 0;
- constexpr std::string_view magicSelector = "\xf0\x9f\xa4\xaf";
- dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter = makeVMAddrConverter(false);
- uint64_t sharedCacheSlide = (uint64_t)this - unslidLoadAddress();
- forEachImage(^(const mach_header *mh, const char *installName) {
- if ( !strcmp(installName, "/usr/lib/libobjc.A.dylib") ) {
- const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)mh;
- Diagnostics diag;
- ma->forEachObjCSelectorReference(diag, vmAddrConverter,
- ^(uint64_t selRefVMAddr, uint64_t selRefTargetVMAddr, bool &stop) {
- const char* selValue = (const char*)(selRefTargetVMAddr + sharedCacheSlide);
- if ( selValue == magicSelector ) {
- sharedCacheRelativeSelectorBaseVMAddress = selRefTargetVMAddr;
- stop = true;
- }
- });
- }
- });
- return sharedCacheRelativeSelectorBaseVMAddress;
+ uint64_t vmOffset = (uint64_t)value - (uint64_t)this;
+ return this->unslidLoadAddress() + vmOffset;
}
#endif
@@ -2233,3 +2210,5 @@
});
}
#endif
+
+#endif // !TARGET_OS_EXCLAVEKIT