unused : 62; // all zeros
};
struct SharedCache {
- uint64_t kind : 2, // kindSharedCache
- offset : 62;
+ uint64_t kind : 2; // kindSharedCache
+ int64_t offset : 62;
};
struct Image {
uint64_t kind : 2, // kindImage
- imageNum : 22, // ImageNum
- offset : 40;
+ imageNum : 22; // ImageNum
+ int64_t offset : 40;
};
struct Absolute {
uint64_t kind : 2, // kindAbsolute
return (raw != rhs.raw);
}
};
+ static_assert(sizeof(ResolvedSymbolTarget) == 8);
// ObjC optimisations
}
else if ( isWeak ) {
_dependencies.push_back(Image::LinkedImage(Image::LinkKind::weak, kMissingWeakLinkedImage));
+ // <rdar://problem/54387345> don't let an error loading weak dylib cause everything to fail
+ // _diag is checked after each dependent load, so if there is an error it was with loading the current dylib.
+ // Since it is a weak load, it is ok to ignore and and go on.
+ _diag.clearError();
}
else {
BLOCK_ACCCESSIBLE_ARRAY(char, extra, 4096);
target.absolute.value = foundInfo.value + addend;
}
else if ( impDylib->inDyldCache() ) {
+ uint64_t offsetValue = (uint8_t*)impDylib - (uint8_t*)_dyldCache + foundInfo.value + addend;
target.sharedCache.kind = Image::ResolvedSymbolTarget::kindSharedCache;
- target.sharedCache.offset = (uint8_t*)impDylib - (uint8_t*)_dyldCache + foundInfo.value + addend;
+ target.sharedCache.offset = offsetValue;
+ assert(target.sharedCache.offset == offsetValue);
}
else {
+ uint64_t offsetValue = foundInfo.value + addend;
target.image.kind = Image::ResolvedSymbolTarget::kindImage;
target.image.imageNum = findLoadedImage(impDylib).imageNum;
- target.image.offset = foundInfo.value + addend;
+ target.image.offset = offsetValue;
+ assert(target.image.offset == offsetValue);
}
return true;
}
};
enum {
- CS_PAGE_SIZE = 4096,
+ CS_PAGE_SIZE_4K = 4096,
+ CS_PAGE_SIZE_16K = 16384,
CS_HASHTYPE_SHA1 = 1,
CS_HASHTYPE_SHA256 = 2,
}
void MachOAnalyzer::recurseTrie(Diagnostics& diag, const uint8_t* const start, const uint8_t* p, const uint8_t* const end,
- char* cummulativeString, int curStrOffset, bool& stop, ExportsCallback callback) const
+ OverflowSafeArray<char>& cummulativeString, int curStrOffset, bool& stop, ExportsCallback callback) const
{
if ( p >= end ) {
diag.error("malformed trie, node past end");
}
if ( diag.hasError() )
return;
- callback(cummulativeString, imageOffset, flags, other, importName, stop);
+ callback(cummulativeString.begin(), imageOffset, flags, other, importName, stop);
if ( stop )
return;
}
for (uint8_t i=0; i < childrenCount; ++i) {
int edgeStrLen = 0;
while (*s != '\0') {
+ cummulativeString.resize(curStrOffset+edgeStrLen + 1);
cummulativeString[curStrOffset+edgeStrLen] = *s++;
++edgeStrLen;
if ( s > end ) {
return;
}
}
+ cummulativeString.resize(curStrOffset+edgeStrLen + 1);
cummulativeString[curStrOffset+edgeStrLen] = *s++;
uint64_t childNodeOffset = read_uleb128(diag, s, end);
if (childNodeOffset == 0) {
if ( const uint8_t* trieStart = getExportsTrie(leInfo, trieSize) ) {
const uint8_t* trieEnd = trieStart + trieSize;
bool stop = false;
- char cummulativeString[trieSize];
+ STACK_ALLOC_OVERFLOW_SAFE_ARRAY(char, cummulativeString, 4096);
recurseTrie(diag, trieStart, trieStart, trieEnd, cummulativeString, 0, stop, callback);
}
}
return firstSectionFileOffset - firstSegmentFileOffset - existSpaceUsed;
}
+void MachOAnalyzer::forEachWeakDef(Diagnostics& diag,
+ void (^handler)(const char* symbolName, uintptr_t imageOffset, bool isFromExportTrie)) const {
+ uint64_t baseAddress = preferredLoadAddress();
+ forEachGlobalSymbol(diag, ^(const char *symbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool &stop) {
+ if ( (n_desc & N_WEAK_DEF) != 0 ) {
+ handler(symbolName, n_value - baseAddress, false);
+ }
+ });
+ forEachExportedSymbol(diag, ^(const char *symbolName, uint64_t imageOffset, uint64_t flags, uint64_t other, const char *importName, bool &stop) {
+ if ( (flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION ) == 0 )
+ return;
+ // Skip resolvers and re-exports
+ if ( (flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) != 0 )
+ return;
+ if ( (flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) != 0 )
+ return;
+ handler(symbolName, imageOffset, true);
+ });
+}
} // dyld3
bool hasObjCMessageReferences() const;
const ObjCImageInfo* objcImageInfo() const;
+
+ void forEachWeakDef(Diagnostics& diag, void (^handler)(const char* symbolName, uintptr_t imageOffset, bool isFromExportTrie)) const;
private:
bool contentIsRegularStub(const uint8_t* helperContent) const;
uint64_t entryAddrFromThreadCmd(const thread_command* cmd) const;
void recurseTrie(Diagnostics& diag, const uint8_t* const start, const uint8_t* p, const uint8_t* const end,
- char* cummulativeString, int curStrOffset, bool& stop, MachOAnalyzer::ExportsCallback callback) const;
+ OverflowSafeArray<char>& cummulativeString, int curStrOffset, bool& stop, MachOAnalyzer::ExportsCallback callback) const;
void analyzeSegmentsLayout(uint64_t& vmSpace, bool& hasZeroFill) const;
};
#endif
const CacheBuilder::ArchLayout CacheBuilder::_s_archLayout[] = {
- { 0x7FFF20000000ULL, 0xEFE00000ULL, 0x0, 0x40000000, 0x00FFFF0000000000, "x86_64", 12, 2, true, true, true },
- { 0x7FFF20000000ULL, 0xEFE00000ULL, 0x0, 0x40000000, 0x00FFFF0000000000, "x86_64h", 12, 2, true, true, true },
- { SHARED_REGION_BASE_I386, SHARED_REGION_SIZE_I386, 0x0, 0x00200000, 0x0, "i386", 12, 0, false, false, true },
- { ARM64_SHARED_REGION_START, ARM64_SHARED_REGION_SIZE, 0x0, 0x02000000, 0x00FFFF0000000000, "arm64", 14, 2, false, true, false },
+ { 0x7FFF20000000ULL, 0xEFE00000ULL, 0x0, 0x40000000, 0x00FFFF0000000000, "x86_64", CS_PAGE_SIZE_4K, 12, 2, true, true, true },
+ { 0x7FFF20000000ULL, 0xEFE00000ULL, 0x0, 0x40000000, 0x00FFFF0000000000, "x86_64h", CS_PAGE_SIZE_4K, 12, 2, true, true, true },
+ { SHARED_REGION_BASE_I386, SHARED_REGION_SIZE_I386, 0x0, 0x00200000, 0x0, "i386", CS_PAGE_SIZE_4K, 12, 0, false, false, true },
+ { ARM64_SHARED_REGION_START, ARM64_SHARED_REGION_SIZE, 0x0, 0x02000000, 0x00FFFF0000000000, "arm64", CS_PAGE_SIZE_4K, 14, 2, false, true, false },
#if SUPPORT_ARCH_arm64e
- { ARM64_SHARED_REGION_START, ARM64_SHARED_REGION_SIZE, 0x0, 0x02000000, 0x00FFFF0000000000, "arm64e", 14, 2, false, true, false },
+ { ARM64_SHARED_REGION_START, ARM64_SHARED_REGION_SIZE, 0x0, 0x02000000, 0x00FFFF0000000000, "arm64e", CS_PAGE_SIZE_16K, 14, 2, false, true, false },
#endif
#if SUPPORT_ARCH_arm64_32
- { ARM64_32_SHARED_REGION_START, ARM64_32_SHARED_REGION_SIZE,0x0, 0x02000000, 0xC0000000, "arm64_32", 14, 6, false, false, true },
+ { ARM64_32_SHARED_REGION_START, ARM64_32_SHARED_REGION_SIZE,0x0, 0x02000000, 0xC0000000, "arm64_32", CS_PAGE_SIZE_16K, 14, 6, false, false, true },
#endif
- { ARM_SHARED_REGION_START, ARM_SHARED_REGION_SIZE, 0x0, 0x02000000, 0xE0000000, "armv7s", 14, 4, false, false, true },
- { ARM_SHARED_REGION_START, ARM_SHARED_REGION_SIZE, ARMV7K_MAX, 0x00400000, ARMV7K_CHAIN_BITS, "armv7k", 14, 4, false, false, true },
- { 0x40000000, 0x40000000, 0x0, 0x02000000, 0x0, "sim-x86", 14, 0, false, false, true }
+ { ARM_SHARED_REGION_START, ARM_SHARED_REGION_SIZE, 0x0, 0x02000000, 0xE0000000, "armv7s", CS_PAGE_SIZE_4K, 14, 4, false, false, true },
+ { ARM_SHARED_REGION_START, ARM_SHARED_REGION_SIZE, ARMV7K_MAX, 0x00400000, ARMV7K_CHAIN_BITS, "armv7k", CS_PAGE_SIZE_4K, 14, 4, false, false, true },
+ { 0x40000000, 0x40000000, 0x0, 0x02000000, 0x0, "sim-x86", CS_PAGE_SIZE_4K, 14, 0, false, false, true }
};
// get pointers into shared cache buffer
size_t inBbufferSize = _readExecuteRegion.sizeInUse+_readWriteRegion.sizeInUse+_readOnlyRegion.sizeInUse+_localSymbolsRegion.sizeInUse;
+ const uint16_t pageSize = _archLayout->csPageSize;
+
// layout code signature contents
uint32_t blobCount = agile ? 4 : 3;
size_t idSize = cacheIdentifier.size()+1; // +1 for terminating 0
- uint32_t slotCount = (uint32_t)((inBbufferSize + CS_PAGE_SIZE - 1) / CS_PAGE_SIZE);
+ uint32_t slotCount = (uint32_t)((inBbufferSize + pageSize - 1) / pageSize);
uint32_t xSlotCount = CSSLOT_REQUIREMENTS;
size_t idOffset = offsetof(CS_CodeDirectory, end_withExecSeg);
size_t hashOffset = idOffset+idSize + dscHashSize*xSlotCount;
cd->hashSize = dscHashSize;
cd->hashType = dscHashType;
cd->platform = 0; // not platform binary
- cd->pageSize = __builtin_ctz(CS_PAGE_SIZE); // log2(CS_PAGE_SIZE);
+ cd->pageSize = __builtin_ctz(pageSize); // log2(CS_PAGE_SIZE);
cd->spare2 = 0; // unused (must be zero)
cd->scatterOffset = 0; // not supported anymore
cd->teamOffset = 0; // no team ID
cd256->hashSize = CS_HASH_SIZE_SHA256;
cd256->hashType = CS_HASHTYPE_SHA256;
cd256->platform = 0; // not platform binary
- cd256->pageSize = __builtin_ctz(CS_PAGE_SIZE); // log2(CS_PAGE_SIZE);
+ cd256->pageSize = __builtin_ctz(pageSize); // log2(CS_PAGE_SIZE);
cd256->spare2 = 0; // unused (must be zero)
cd256->scatterOffset = 0; // not supported anymore
cd256->teamOffset = 0; // no team ID
cache->codeSignatureOffset = inBbufferSize;
cache->codeSignatureSize = sigSize;
- const uint32_t rwSlotStart = (uint32_t)(_readExecuteRegion.sizeInUse / CS_PAGE_SIZE);
- const uint32_t roSlotStart = (uint32_t)(rwSlotStart + _readWriteRegion.sizeInUse / CS_PAGE_SIZE);
- const uint32_t localsSlotStart = (uint32_t)(roSlotStart + _readOnlyRegion.sizeInUse / CS_PAGE_SIZE);
+ const uint32_t rwSlotStart = (uint32_t)(_readExecuteRegion.sizeInUse / pageSize);
+ const uint32_t roSlotStart = (uint32_t)(rwSlotStart + _readWriteRegion.sizeInUse / pageSize);
+ const uint32_t localsSlotStart = (uint32_t)(roSlotStart + _readOnlyRegion.sizeInUse / pageSize);
auto codeSignPage = ^(size_t i) {
const uint8_t* code = nullptr;
// move to correct region
if ( i < rwSlotStart )
- code = _readExecuteRegion.buffer + (i * CS_PAGE_SIZE);
+ code = _readExecuteRegion.buffer + (i * pageSize);
else if ( i >= rwSlotStart && i < roSlotStart )
- code = _readWriteRegion.buffer + ((i - rwSlotStart) * CS_PAGE_SIZE);
+ code = _readWriteRegion.buffer + ((i - rwSlotStart) * pageSize);
else if ( i >= roSlotStart && i < localsSlotStart )
- code = _readOnlyRegion.buffer + ((i - roSlotStart) * CS_PAGE_SIZE);
+ code = _readOnlyRegion.buffer + ((i - roSlotStart) * pageSize);
else
- code = _localSymbolsRegion.buffer + ((i - localsSlotStart) * CS_PAGE_SIZE);
+ code = _localSymbolsRegion.buffer + ((i - localsSlotStart) * pageSize);
- CCDigest(dscDigestFormat, code, CS_PAGE_SIZE, hashSlot + (i * dscHashSize));
+ CCDigest(dscDigestFormat, code, pageSize, hashSlot + (i * dscHashSize));
if ( agile ) {
- CCDigest(kCCDigestSHA256, code, CS_PAGE_SIZE, hash256Slot + (i * CS_HASH_SIZE_SHA256));
+ CCDigest(kCCDigestSHA256, code, pageSize, hash256Slot + (i * CS_HASH_SIZE_SHA256));
}
};
{
uint8_t* uuidLoc = cache->uuid;
assert(uuid_is_null(uuidLoc));
- static_assert(offsetof(dyld_cache_header, uuid) / CS_PAGE_SIZE == 0, "uuid is expected in the first page of the cache");
+ static_assert(offsetof(dyld_cache_header, uuid) / CS_PAGE_SIZE_4K == 0, "uuid is expected in the first page of the cache");
uint8_t fullDigest[CC_SHA256_DIGEST_LENGTH];
CC_SHA256((const void*)cd, (unsigned)cdSize, fullDigest);
memcpy(uuidLoc, fullDigest, 16);
uint64_t sharedRegionPadding;
uint64_t pointerDeltaMask;
const char* archName;
+ uint16_t csPageSize;
uint8_t sharedRegionAlignP2;
uint8_t slideInfoBytesPerPage;
bool sharedRegionsAreDiscontiguous;
size_t inBbufferSize = 0;
for (auto& sharedCacheRegion : sharedCacheRegions)
inBbufferSize += (sharedCacheRegion.second - sharedCacheRegion.first);
- uint32_t slotCountFromRegions = (uint32_t)((inBbufferSize + CS_PAGE_SIZE - 1) / CS_PAGE_SIZE);
// Now take the cd hash from the cache itself and validate the regions we found.
uint8_t* codeSignatureRegion = (uint8_t*)mapped_cache + dyldSharedCache->header.codeSignatureOffset;
return -1;
}
+ uint32_t pageSize = 1 << cd->pageSize;
+ uint32_t slotCountFromRegions = (uint32_t)((inBbufferSize + pageSize - 1) / pageSize);
if ( ntohl(cd->nCodeSlots) < slotCountFromRegions ) {
fprintf(stderr, "Error: dyld shared cache code signature directory num slots is incorrect.\n");
return -1;
continue;
inBbufferSize += (sharedCacheRegion.second - sharedCacheRegion.first);
}
- uint32_t slotCountToProcess = (uint32_t)((inBbufferSize + CS_PAGE_SIZE - 1) / CS_PAGE_SIZE);
+ uint32_t slotCountToProcess = (uint32_t)((inBbufferSize + pageSize - 1) / pageSize);
for (unsigned i = 0; i != slotCountToProcess; ++i) {
// Skip data pages as those may have been slid by ASLR in the extracted file
#include <sys/mount.h>
#include <sys/sysctl.h>
#include <libkern/OSAtomic.h>
+#include <string_view>
#include <atomic>
"__ZdaPvSt11align_val_t", "__ZdaPvSt11align_val_tRKSt9nothrow_t", "__ZdaPvmSt11align_val_t"
};
+size_t ImageLoader::HashCString::hash(const char* v) {
+ // FIXME: Use hash<string_view> when it has the correct visibility markup
+ return std::hash<std::string_view>{}(v);
+}
+
+bool ImageLoader::EqualCString::equal(const char* s1, const char* s2) {
+ return strcmp(s1, s2) == 0;
+}
void ImageLoader::weakBind(const LinkContext& context)
{
+
+ if (!context.useNewWeakBind) {
+ weakBindOld(context);
+ return;
+ }
+
if ( context.verboseWeakBind )
dyld::log("dyld: weak bind start:\n");
uint64_t t1 = mach_absolute_time();
+
// get set of ImageLoaders that participate in coalecsing
ImageLoader* imagesNeedingCoalescing[fgImagesRequiringCoalescing];
unsigned imageIndexes[fgImagesRequiringCoalescing];
++countOfImagesWithWeakDefinitionsNotInSharedCache;
}
+ // don't need to do any coalescing if only one image has overrides, or all have already been done
+ if ( (countOfImagesWithWeakDefinitionsNotInSharedCache > 0) && (countNotYetWeakBound > 0) ) {
+ if (!context.weakDefMapInitialized) {
+ // Initialize the weak def map as the link context doesn't run static initializers
+ new (&context.weakDefMap) dyld3::Map<const char*, std::pair<const ImageLoader*, uintptr_t>, ImageLoader::HashCString, ImageLoader::EqualCString>();
+ context.weakDefMapInitialized = true;
+ }
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+ // only do alternate algorithm for dlopen(). Use traditional algorithm for launch
+ if ( !context.linkingMainExecutable ) {
+ // Don't take the memory hit of weak defs on the launch path until we hit a dlopen with more weak symbols to bind
+ if (!context.weakDefMapProcessedLaunchDefs) {
+ context.weakDefMapProcessedLaunchDefs = true;
+
+ // Walk the nlist for all binaries from launch and fill in the map with any other weak defs
+ for (int i=0; i < count; ++i) {
+ const ImageLoader* image = imagesNeedingCoalescing[i];
+ // skip images without defs. We've processed launch time refs already
+ if ( !image->hasCoalescedExports() )
+ continue;
+ // Only process binaries which have had their weak symbols bound, ie, not the new ones we are processing now
+ // from this dlopen
+ if ( !image->weakSymbolsBound(imageIndexes[i]) )
+ continue;
+
+ Diagnostics diag;
+ const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)image->machHeader();
+ ma->forEachWeakDef(diag, ^(const char *symbolName, uintptr_t imageOffset, bool isFromExportTrie) {
+ uintptr_t targetAddr = (uintptr_t)ma + imageOffset;
+ if ( isFromExportTrie ) {
+ // Avoid duplicating the string if we already have the symbol name
+ if ( context.weakDefMap.find(symbolName) != context.weakDefMap.end() )
+ return;
+ symbolName = strdup(symbolName);
+ }
+ context.weakDefMap.insert({ symbolName, { image, targetAddr } });
+ });
+ }
+ }
+
+ // Walk the nlist for all binaries in dlopen and fill in the map with any other weak defs
+ for (int i=0; i < count; ++i) {
+ const ImageLoader* image = imagesNeedingCoalescing[i];
+ if ( image->weakSymbolsBound(imageIndexes[i]) )
+ continue;
+ // skip images without defs. We'll process refs later
+ if ( !image->hasCoalescedExports() )
+ continue;
+ Diagnostics diag;
+ const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)image->machHeader();
+ ma->forEachWeakDef(diag, ^(const char *symbolName, uintptr_t imageOffset, bool isFromExportTrie) {
+ uintptr_t targetAddr = (uintptr_t)ma + imageOffset;
+ if ( isFromExportTrie ) {
+ // Avoid duplicating the string if we already have the symbol name
+ if ( context.weakDefMap.find(symbolName) != context.weakDefMap.end() )
+ return;
+ symbolName = strdup(symbolName);
+ }
+ context.weakDefMap.insert({ symbolName, { image, targetAddr } });
+ });
+ }
+ // for all images that need weak binding
+ for (int i=0; i < count; ++i) {
+ ImageLoader* imageBeingFixedUp = imagesNeedingCoalescing[i];
+ if ( imageBeingFixedUp->weakSymbolsBound(imageIndexes[i]) )
+ continue; // weak binding already completed
+ bool imageBeingFixedUpInCache = imageBeingFixedUp->inSharedCache();
+
+ if ( context.verboseWeakBind )
+ dyld::log("dyld: checking for weak symbols in %s\n", imageBeingFixedUp->getPath());
+ // for all symbols that need weak binding in this image
+ ImageLoader::CoalIterator coalIterator;
+ imageBeingFixedUp->initializeCoalIterator(coalIterator, i, imageIndexes[i]);
+ while ( !imageBeingFixedUp->incrementCoalIterator(coalIterator) ) {
+ const char* nameToCoalesce = coalIterator.symbolName;
+ uintptr_t targetAddr = 0;
+ const ImageLoader* targetImage;
+ // Seatch the map for a previous definition to use
+ auto weakDefIt = context.weakDefMap.find(nameToCoalesce);
+ if ( (weakDefIt != context.weakDefMap.end()) && (weakDefIt->second.first != nullptr) ) {
+ // Found a previous defition
+ targetImage = weakDefIt->second.first;
+ targetAddr = weakDefIt->second.second;
+ } else {
+ // scan all images looking for definition to use
+ for (int j=0; j < count; ++j) {
+ const ImageLoader* anImage = imagesNeedingCoalescing[j];
+ bool anImageInCache = anImage->inSharedCache();
+ // <rdar://problem/47986398> Don't look at images in dyld cache because cache is
+ // already coalesced. Only images outside cache can potentially override something in cache.
+ if ( anImageInCache && imageBeingFixedUpInCache )
+ continue;
+
+ //dyld::log("looking for %s in %s\n", nameToCoalesce, anImage->getPath());
+ const ImageLoader* foundIn;
+ const Symbol* sym = anImage->findExportedSymbol(nameToCoalesce, false, &foundIn);
+ if ( sym != NULL ) {
+ targetAddr = foundIn->getExportedSymbolAddress(sym, context);
+ targetImage = foundIn;
+ if ( context.verboseWeakBind )
+ dyld::log("dyld: found weak %s at 0x%lX in %s\n", nameToCoalesce, targetAddr, foundIn->getPath());
+ break;
+ }
+ }
+ }
+ if ( (targetAddr != 0) && (coalIterator.image != targetImage) ) {
+ coalIterator.image->updateUsesCoalIterator(coalIterator, targetAddr, (ImageLoader*)targetImage, 0, context);
+ if (weakDefIt == context.weakDefMap.end()) {
+ if (targetImage->neverUnload()) {
+ // Add never unload defs to the map for next time
+ context.weakDefMap.insert({ nameToCoalesce, { targetImage, targetAddr } });
+ if ( context.verboseWeakBind ) {
+ dyld::log("dyld: weak binding adding %s to map\n", nameToCoalesce);
+ }
+ } else {
+ // Add a placeholder for unloadable symbols which makes us fall back to the regular search
+ context.weakDefMap.insert({ nameToCoalesce, { targetImage, targetAddr } });
+ if ( context.verboseWeakBind ) {
+ dyld::log("dyld: weak binding adding unloadable placeholder %s to map\n", nameToCoalesce);
+ }
+ }
+ }
+ if ( context.verboseWeakBind )
+ dyld::log("dyld: adjusting uses of %s in %s to use definition from %s\n", nameToCoalesce, coalIterator.image->getPath(), targetImage->getPath());
+ }
+ }
+ imageBeingFixedUp->setWeakSymbolsBound(imageIndexes[i]);
+ }
+ }
+ else
+#endif // __MAC_OS_X_VERSION_MIN_REQUIRED
+ {
+ // make symbol iterators for each
+ ImageLoader::CoalIterator iterators[count];
+ ImageLoader::CoalIterator* sortedIts[count];
+ for(int i=0; i < count; ++i) {
+ imagesNeedingCoalescing[i]->initializeCoalIterator(iterators[i], i, imageIndexes[i]);
+ sortedIts[i] = &iterators[i];
+ if ( context.verboseWeakBind )
+ dyld::log("dyld: weak bind load order %d/%d for %s\n", i, count, imagesNeedingCoalescing[i]->getIndexedPath(imageIndexes[i]));
+ }
+
+ // walk all symbols keeping iterators in sync by
+ // only ever incrementing the iterator with the lowest symbol
+ int doneCount = 0;
+ while ( doneCount != count ) {
+ //for(int i=0; i < count; ++i)
+ // dyld::log("sym[%d]=%s ", sortedIts[i]->loadOrder, sortedIts[i]->symbolName);
+ //dyld::log("\n");
+ // increment iterator with lowest symbol
+ if ( sortedIts[0]->image->incrementCoalIterator(*sortedIts[0]) )
+ ++doneCount;
+ // re-sort iterators
+ for(int i=1; i < count; ++i) {
+ int result = strcmp(sortedIts[i-1]->symbolName, sortedIts[i]->symbolName);
+ if ( result == 0 )
+ sortedIts[i-1]->symbolMatches = true;
+ if ( result > 0 ) {
+ // new one is bigger then next, so swap
+ ImageLoader::CoalIterator* temp = sortedIts[i-1];
+ sortedIts[i-1] = sortedIts[i];
+ sortedIts[i] = temp;
+ }
+ if ( result < 0 )
+ break;
+ }
+ // process all matching symbols just before incrementing the lowest one that matches
+ if ( sortedIts[0]->symbolMatches && !sortedIts[0]->done ) {
+ const char* nameToCoalesce = sortedIts[0]->symbolName;
+ // pick first symbol in load order (and non-weak overrides weak)
+ uintptr_t targetAddr = 0;
+ ImageLoader* targetImage = NULL;
+ unsigned targetImageIndex = 0;
+ for(int i=0; i < count; ++i) {
+ if ( strcmp(iterators[i].symbolName, nameToCoalesce) == 0 ) {
+ if ( context.verboseWeakBind )
+ dyld::log("dyld: weak bind, found %s weak=%d in %s \n", nameToCoalesce, iterators[i].weakSymbol, iterators[i].image->getIndexedPath((unsigned)iterators[i].imageIndex));
+ if ( iterators[i].weakSymbol ) {
+ if ( targetAddr == 0 ) {
+ targetAddr = iterators[i].image->getAddressCoalIterator(iterators[i], context);
+ if ( targetAddr != 0 ) {
+ targetImage = iterators[i].image;
+ targetImageIndex = (unsigned)iterators[i].imageIndex;
+ }
+ }
+ }
+ else {
+ targetAddr = iterators[i].image->getAddressCoalIterator(iterators[i], context);
+ if ( targetAddr != 0 ) {
+ targetImage = iterators[i].image;
+ targetImageIndex = (unsigned)iterators[i].imageIndex;
+ // strong implementation found, stop searching
+ break;
+ }
+ }
+ }
+ }
+ // tell each to bind to this symbol (unless already bound)
+ if ( targetAddr != 0 ) {
+ if ( context.verboseWeakBind ) {
+ dyld::log("dyld: weak binding all uses of %s to copy from %s\n",
+ nameToCoalesce, targetImage->getIndexedShortName(targetImageIndex));
+ }
+ for(int i=0; i < count; ++i) {
+ if ( strcmp(iterators[i].symbolName, nameToCoalesce) == 0 ) {
+ if ( context.verboseWeakBind ) {
+ dyld::log("dyld: weak bind, setting all uses of %s in %s to 0x%lX from %s\n",
+ nameToCoalesce, iterators[i].image->getIndexedShortName((unsigned)iterators[i].imageIndex),
+ targetAddr, targetImage->getIndexedShortName(targetImageIndex));
+ }
+ if ( ! iterators[i].image->weakSymbolsBound(imageIndexes[i]) )
+ iterators[i].image->updateUsesCoalIterator(iterators[i], targetAddr, targetImage, targetImageIndex, context);
+ iterators[i].symbolMatches = false;
+ }
+ }
+ if (targetImage->neverUnload()) {
+ // Add never unload defs to the map for next time
+ context.weakDefMap.insert({ nameToCoalesce, { targetImage, targetAddr } });
+ if ( context.verboseWeakBind ) {
+ dyld::log("dyld: weak binding adding %s to map\n",
+ nameToCoalesce);
+ }
+ }
+ }
+
+ }
+ }
+
+ for (int i=0; i < count; ++i) {
+ if ( imagesNeedingCoalescing[i]->weakSymbolsBound(imageIndexes[i]) )
+ continue; // skip images already processed
+
+ if ( imagesNeedingCoalescing[i]->usesChainedFixups() ) {
+ // during binding of references to weak-def symbols, the dyld cache was patched
+ // but if main executable has non-weak override of operator new or delete it needs is handled here
+ for (const char* weakSymbolName : sTreatAsWeak) {
+ const ImageLoader* dummy;
+ imagesNeedingCoalescing[i]->resolveWeak(context, weakSymbolName, true, false, &dummy);
+ }
+ }
+#if __arm64e__
+ else {
+ // support traditional arm64 app on an arm64e device
+ // look for weak def symbols in this image which may override the cache
+ ImageLoader::CoalIterator coaler;
+ imagesNeedingCoalescing[i]->initializeCoalIterator(coaler, i, 0);
+ imagesNeedingCoalescing[i]->incrementCoalIterator(coaler);
+ while ( !coaler.done ) {
+ const ImageLoader* dummy;
+ // a side effect of resolveWeak() is to patch cache
+ imagesNeedingCoalescing[i]->resolveWeak(context, coaler.symbolName, true, false, &dummy);
+ imagesNeedingCoalescing[i]->incrementCoalIterator(coaler);
+ }
+ }
+#endif
+ }
+
+ // mark all as having all weak symbols bound
+ for(int i=0; i < count; ++i) {
+ imagesNeedingCoalescing[i]->setWeakSymbolsBound(imageIndexes[i]);
+ }
+ }
+ }
+
+ uint64_t t2 = mach_absolute_time();
+ fgTotalWeakBindTime += t2 - t1;
+
+ if ( context.verboseWeakBind )
+ dyld::log("dyld: weak bind end\n");
+}
+
+
+void ImageLoader::weakBindOld(const LinkContext& context)
+{
+ if ( context.verboseWeakBind )
+ dyld::log("dyld: weak bind start:\n");
+ uint64_t t1 = mach_absolute_time();
+ // get set of ImageLoaders that participate in coalecsing
+ ImageLoader* imagesNeedingCoalescing[fgImagesRequiringCoalescing];
+ unsigned imageIndexes[fgImagesRequiringCoalescing];
+ int count = context.getCoalescedImages(imagesNeedingCoalescing, imageIndexes);
+
+ // count how many have not already had weakbinding done
+ int countNotYetWeakBound = 0;
+ int countOfImagesWithWeakDefinitionsNotInSharedCache = 0;
+ for(int i=0; i < count; ++i) {
+ if ( ! imagesNeedingCoalescing[i]->weakSymbolsBound(imageIndexes[i]) )
+ ++countNotYetWeakBound;
+ if ( ! imagesNeedingCoalescing[i]->inSharedCache() )
+ ++countOfImagesWithWeakDefinitionsNotInSharedCache;
+ }
+
// don't need to do any coalescing if only one image has overrides, or all have already been done
if ( (countOfImagesWithWeakDefinitionsNotInSharedCache > 0) && (countNotYetWeakBound > 0) ) {
#if __MAC_OS_X_VERSION_MIN_REQUIRED
if ( context.verboseWeakBind )
dyld::log("dyld: weak bind load order %d/%d for %s\n", i, count, imagesNeedingCoalescing[i]->getIndexedPath(imageIndexes[i]));
}
-
- // walk all symbols keeping iterators in sync by
- // only ever incrementing the iterator with the lowest symbol
+
+ // walk all symbols keeping iterators in sync by
+ // only ever incrementing the iterator with the lowest symbol
int doneCount = 0;
while ( doneCount != count ) {
//for(int i=0; i < count; ++i)
//dyld::log("\n");
// increment iterator with lowest symbol
if ( sortedIts[0]->image->incrementCoalIterator(*sortedIts[0]) )
- ++doneCount;
+ ++doneCount;
// re-sort iterators
for(int i=1; i < count; ++i) {
int result = strcmp(sortedIts[i-1]->symbolName, sortedIts[i]->symbolName);
}
if ( ! iterators[i].image->weakSymbolsBound(imageIndexes[i]) )
iterators[i].image->updateUsesCoalIterator(iterators[i], targetAddr, targetImage, targetImageIndex, context);
- iterators[i].symbolMatches = false;
+ iterators[i].symbolMatches = false;
}
}
}
uint64_t t2 = mach_absolute_time();
fgTotalWeakBindTime += t2 - t1;
-
+
if ( context.verboseWeakBind )
dyld::log("dyld: weak bind end\n");
}
#include "DyldSharedCache.h"
+#include "Map.h"
+
#if __arm__
#include <mach/vm_page_size.h>
#endif
};
typedef void (^CoalesceNotifier)(const Symbol* implSym, const ImageLoader* implIn, const mach_header* implMh);
+
+ struct HashCString {
+ static size_t hash(const char* v);
+ };
+
+ struct EqualCString {
+ static bool equal(const char* s1, const char* s2);
+ };
struct LinkContext {
ImageLoader* (*loadLibrary)(const char* libraryName, bool search, const char* origin, const RPathChain* rpaths, unsigned& cacheIndex);
size_t dynamicInterposeCount;
PrebindMode prebindUsage;
SharedRegionMode sharedRegionMode;
+ mutable dyld3::Map<const char*, std::pair<const ImageLoader*, uintptr_t>, HashCString, EqualCString> weakDefMap;
+ mutable bool weakDefMapInitialized = false;
+ mutable bool weakDefMapProcessedLaunchDefs = false;
+ mutable bool useNewWeakBind = false;
bool dyldLoadedAtSameAddressNeededBySharedCache;
bool strictMachORequired;
bool allowAtPaths;
// in mach-o a parent library knows name of sub libraries it re-exports..
virtual bool hasSubLibrary(const LinkContext& context, const ImageLoader* child) const = 0;
- virtual bool weakSymbolsBound(unsigned index) { return fWeakSymbolsBound; }
+ virtual bool weakSymbolsBound(unsigned index) const { return fWeakSymbolsBound; }
virtual void setWeakSymbolsBound(unsigned index) { fWeakSymbolsBound = true; }
// set fState to dyld_image_state_memory_mapped
void processInitializers(const LinkContext& context, mach_port_t this_thread,
InitializerTimingList& timingInfo, ImageLoader::UninitedUpwards& ups);
+ void weakBindOld(const LinkContext& context);
+
recursive_lock* fInitializerRecursiveLock;
union {
}
-bool ImageLoaderMegaDylib::weakSymbolsBound(unsigned index)
+bool ImageLoaderMegaDylib::weakSymbolsBound(unsigned index) const
{
return ( _stateFlags[index] >= kStateFlagWeakBound );
}
bool allDependentLibrariesAsWhenPreBound() const { unreachable(); }
virtual bool isSubframeworkOf(const LinkContext& context, const ImageLoader* image) const { return false; }
virtual bool hasSubLibrary(const LinkContext& context, const ImageLoader* child) const { return false; }
- virtual bool weakSymbolsBound(unsigned index);
+ virtual bool weakSymbolsBound(unsigned index) const;
virtual void setWeakSymbolsBound(unsigned index);
private:
}
}
+ // If this image is the potential canonical definition of any weak defs, then set them to a tombstone value
+ if ( gLinkContext.weakDefMapInitialized && image->hasCoalescedExports() ) {
+ Diagnostics diag;
+ const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)image->machHeader();
+ ma->forEachWeakDef(diag, ^(const char *symbolName, uintptr_t imageOffset, bool isFromExportTrie) {
+ auto it = gLinkContext.weakDefMap.find(symbolName);
+ assert(it != gLinkContext.weakDefMap.end());
+ it->second = { nullptr, 0 };
+ if ( !isFromExportTrie ) {
+ // The string was already duplicated if we are an export trie
+ // so only strdup as we are the nlist
+ size_t hash1 = ImageLoader::HashCString::hash(it->first);
+ it->first = strdup(it->first);
+ size_t hash2 = ImageLoader::HashCString::hash(it->first);
+ assert(hash1 == hash2);
+ }
+ });
+ }
+
// log if requested
if ( gLinkContext.verboseLoading || (sEnv.DYLD_PRINT_LIBRARIES_POST_LAUNCH && (sMainExecutable!=NULL) && sMainExecutable->isLinked()) ) {
const char *imagePath = image->getPath();
--- /dev/null
+
+// BUILD: $CC main.c -o $BUILD_DIR/bind-addend.exe -lobjc
+
+// RUN: ./bind-addend.exe
+
+// Verify that negative addends work with pointers in to the shared cache and pointers to the image itself
+
+#include <stdio.h>
+
+// Note this is weak so that we have a bind
+__attribute__((weak))
+void* p = 0;
+
+// Choose a large enough negative offset to be before the shared cache or the image
+const uintptr_t offset = 1ULL << 36;
+void* pMinus = (void*)((uintptr_t)&p - offset);
+
+// Get a pointer to something we assume is in the shared cache
+// Note we don't declare a function as arm64e would want to sign this
+extern int objc_msgSend;
+void* msgSendMinus = (void*)((uintptr_t)&objc_msgSend - offset);
+
+int main()
+{
+ printf("[BEGIN] bind-addend\n");
+
+ if ( pMinus != (void*)((uintptr_t)&p - offset) ) {
+ printf("[FAIL] bind-addend: %p != %p\n", pMinus, (void*)((uintptr_t)&p - offset));
+ return 0;
+ }
+
+ if ( msgSendMinus != (void*)((uintptr_t)&objc_msgSend - offset) ) {
+ printf("[FAIL] bind-addend: %p != %p\n", msgSendMinus, (void*)((uintptr_t)&objc_msgSend - offset));
+ return 0;
+ }
+
+ printf("[PASS] bind-addend\n");
+ return 0;
+}
+
--- /dev/null
+int foo = 42;
--- /dev/null
+
+// BOOT_ARGS: dyld_flags=2
+
+// BUILD: $CC foo.c -dynamiclib -o $TEMP_DIR/libmissing.dylib -install_name @rpath/libmissing.dylib
+// BUILD: $CC foo.c -dynamiclib -Wl,-weak_library,$TEMP_DIR/libmissing.dylib -o $BUILD_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib -rpath $RUN_DIR
+// BUILD: $CC main.c -o $BUILD_DIR/rpath-weak-missing.exe -DRUN_DIR="$RUN_DIR"
+
+// RUN: ./rpath-weak-missing.exe
+// RUN: DYLD_AMFI_FAKE=0 ./rpath-weak-missing.exe
+
+// main prog dlopen()s libfoo.dylib which weak links to @rpath/libmissing.dylib
+
+#include <stdio.h>
+#include <dlfcn.h>
+
+
+int main()
+{
+ printf("[BEGIN] rpath-weak-missing\n");
+
+ void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY);
+ if ( handle == NULL ) {
+ printf("[FAIL] rpath-weak-missing dlopen(\"%s/libfoo.dylib\") - %s\n", RUN_DIR, dlerror());
+ return 0;
+ }
+
+ printf("[PASS] rpath-weak-missing\n");
+ return 0;
+}
+
+
--- /dev/null
+
+__attribute__((weak))
+int foo = 1;
+
+void* fooPtr() {
+ return &foo;
+}
\ No newline at end of file
--- /dev/null
+
+__attribute__((weak))
+int foo() {
+ return 2;
+}
+
+void* fooPtr() {
+ return &foo;
+}
+
+
--- /dev/null
+
+__attribute__((weak))
+int foo() {
+ return 3;
+}
+
+void* fooPtr() {
+ return &foo;
+}
+
+
--- /dev/null
+
+// BUILD: $CC foo1.c -dynamiclib -install_name $RUN_DIR/libfoo1.dylib -o $BUILD_DIR/libfoo1.dylib
+// BUILD: $CC foo2.c -dynamiclib -install_name $RUN_DIR/libfoo2.dylib -o $BUILD_DIR/libfoo2.dylib
+// BUILD: $CC foo3.c -dynamiclib -install_name $RUN_DIR/libfoo3.dylib -o $BUILD_DIR/libfoo3.dylib
+// BUILD: $CC main.c -o $BUILD_DIR/weak-coalesce-unload.exe -DRUN_DIR="$RUN_DIR"
+
+// RUN: ./weak-coalesce-unload.exe
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+
+extern int foo();
+extern void* fooPtr();
+
+int main()
+{
+ printf("[BEGIN] weak-coalesce-unload\n");
+
+ // dlopen foo1 which defines "foo"
+ void* handle1 = dlopen(RUN_DIR "/libfoo1.dylib", RTLD_FIRST);
+ if ( handle1 == NULL ) {
+ printf("[FAIL] dlopen(\"%s\") failed with: %s\n", RUN_DIR "/libfoo1.dylib", dlerror());
+ return 0;
+ }
+
+ const void* symFoo1 = dlsym(handle1, "foo");
+ if ( symFoo1 == NULL ) {
+ printf("[FAIL] dlsym(handle1, foo) failed\n");
+ return 0;
+ }
+
+ const void* symFooPtr1 = dlsym(handle1, "fooPtr");
+ if ( symFooPtr1 == NULL ) {
+ printf("[FAIL] dlsym(handle1, fooPtr) failed\n");
+ return 0;
+ }
+ void* fooptr1 = ((__typeof(&fooPtr))symFooPtr1)();
+
+ int close1 = dlclose(handle1);
+ if ( close1 != 0 ) {
+ printf("[FAIL] dlclose(handle1) failed with: %s\n", dlerror());
+ return 0;
+ }
+
+ // Now dlopen foo2 and get the value it finds for foo
+ void* handle2 = dlopen(RUN_DIR "/libfoo2.dylib", RTLD_FIRST);
+ if ( handle2 == NULL ) {
+ printf("[FAIL] dlopen(\"%s\") failed with: %s\n", RUN_DIR "/libfoo2.dylib", dlerror());
+ return 0;
+ }
+
+ const void* symFoo2 = dlsym(handle2, "foo");
+ if ( symFoo2 == NULL ) {
+ printf("[FAIL] dlsym(handle2, foo) failed\n");
+ return 0;
+ }
+
+ const void* symFooPtr2 = dlsym(handle2, "fooPtr");
+ if ( symFooPtr2 == NULL ) {
+ printf("[FAIL] dlsym(handle2, fooPtr) failed\n");
+ return 0;
+ }
+ void* fooptr2 = ((__typeof(&fooPtr))symFooPtr2)();
+
+ // Don't close foo2, but instead open foo3
+ void* handle3 = dlopen(RUN_DIR "/libfoo3.dylib", RTLD_FIRST);
+ if ( handle3 == NULL ) {
+ printf("[FAIL] dlopen(\"%s\") failed with: %s\n", RUN_DIR "/libfoo3.dylib", dlerror());
+ return 0;
+ }
+
+ const void* symFoo3 = dlsym(handle3, "foo");
+ if ( symFoo3 == NULL ) {
+ printf("[FAIL] dlsym(handle3, foo) failed\n");
+ return 0;
+ }
+
+ const void* symFooPtr3 = dlsym(handle3, "fooPtr");
+ if ( symFooPtr3 == NULL ) {
+ printf("[FAIL] dlsym(handle3, fooPtr) failed\n");
+ return 0;
+ }
+ void* fooptr3 = ((__typeof(&fooPtr))symFooPtr3)();
+
+ // No-one should point to libfoo1.dylib
+ if ( symFoo1 == symFoo2 ) {
+ printf("[FAIL] foo1 == foo2\n");
+ return 0;
+ }
+ if ( symFoo1 == symFoo3 ) {
+ printf("[FAIL] foo1 == foo3\n");
+ return 0;
+ }
+
+ // foo2 and foo3 should be different
+ if ( symFoo2 == symFoo3 ) {
+ printf("[FAIL] foo2 != foo3\n");
+ return 0;
+ }
+
+ // But their coalesced values should be the same
+ if ( fooptr1 == fooptr2 ) {
+ printf("[FAIL] fooptr1 == fooptr2\n");
+ return 0;
+ }
+ if ( fooptr2 != fooptr3 ) {
+ printf("[FAIL] fooptr2 != fooptr3\n");
+ return 0;
+ }
+
+ // foo should return the value from foo2, not the value from foo3
+ // Also calling this would explode if we somehow pointed at foo1
+ if ( ((__typeof(&foo))fooptr2)() != 2 ) {
+ printf("[FAIL] foo2 != 2\n");
+ return 0;
+ }
+ if ( ((__typeof(&foo))fooptr3)() != 2 ) {
+ printf("[FAIL] foo3 != 2\n");
+ return 0;
+ }
+
+ printf("[PASS] weak-coalesce-unload\n");
+ return 0;
+}
+