From a9a4db61c29ec8455dcb07f607391cd82e933936 Mon Sep 17 00:00:00 2001 From: Apple Date: Tue, 24 Mar 2020 21:12:03 +0000 Subject: [PATCH] dyld-733.6.tar.gz --- dyld3/Closure.h | 9 +- dyld3/ClosureBuilder.cpp | 12 +- dyld3/CodeSigningTypes.h | 3 +- dyld3/MachOAnalyzer.cpp | 27 +- dyld3/MachOAnalyzer.h | 4 +- dyld3/shared-cache/CacheBuilder.cpp | 46 +-- dyld3/shared-cache/CacheBuilder.h | 1 + launch-cache/dsc_extractor.cpp | 5 +- src/ImageLoader.cpp | 320 +++++++++++++++++- src/ImageLoader.h | 18 +- src/ImageLoaderMegaDylib.cpp | 2 +- src/ImageLoaderMegaDylib.h | 2 +- src/dyld2.cpp | 19 ++ testing/test-cases/bind-addend.dtest/main.c | 40 +++ .../test-cases/rpath-weak-missing.dtest/foo.c | 1 + .../rpath-weak-missing.dtest/main.c | 31 ++ .../weak-coalesce-unload.dtest/foo1.c | 7 + .../weak-coalesce-unload.dtest/foo2.c | 11 + .../weak-coalesce-unload.dtest/foo3.c | 11 + .../weak-coalesce-unload.dtest/main.c | 127 +++++++ 20 files changed, 652 insertions(+), 44 deletions(-) create mode 100644 testing/test-cases/bind-addend.dtest/main.c create mode 100644 testing/test-cases/rpath-weak-missing.dtest/foo.c create mode 100644 testing/test-cases/rpath-weak-missing.dtest/main.c create mode 100644 testing/test-cases/weak-coalesce-unload.dtest/foo1.c create mode 100644 testing/test-cases/weak-coalesce-unload.dtest/foo2.c create mode 100644 testing/test-cases/weak-coalesce-unload.dtest/foo3.c create mode 100644 testing/test-cases/weak-coalesce-unload.dtest/main.c diff --git a/dyld3/Closure.h b/dyld3/Closure.h index 20d5c0b..4b4eb39 100644 --- a/dyld3/Closure.h +++ b/dyld3/Closure.h @@ -202,13 +202,13 @@ struct VIS_HIDDEN Image : ContainerTypedBytes 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 @@ -227,6 +227,7 @@ struct VIS_HIDDEN Image : ContainerTypedBytes return (raw != rhs.raw); } }; + static_assert(sizeof(ResolvedSymbolTarget) == 8); // ObjC optimisations diff --git a/dyld3/ClosureBuilder.cpp b/dyld3/ClosureBuilder.cpp index f4827f9..daef752 100644 --- a/dyld3/ClosureBuilder.cpp +++ b/dyld3/ClosureBuilder.cpp @@ -672,6 +672,10 @@ void ClosureBuilder::recursiveLoadDependents(LoadedImageChain& forImageChain, bo } else if ( isWeak ) { _dependencies.push_back(Image::LinkedImage(Image::LinkKind::weak, kMissingWeakLinkedImage)); + // 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); @@ -1505,13 +1509,17 @@ bool ClosureBuilder::findSymbolInImage(const MachOAnalyzer* macho, const char* s 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; } diff --git a/dyld3/CodeSigningTypes.h b/dyld3/CodeSigningTypes.h index 22af412..17b4a80 100644 --- a/dyld3/CodeSigningTypes.h +++ b/dyld3/CodeSigningTypes.h @@ -42,7 +42,8 @@ enum { }; enum { - CS_PAGE_SIZE = 4096, + CS_PAGE_SIZE_4K = 4096, + CS_PAGE_SIZE_16K = 16384, CS_HASHTYPE_SHA1 = 1, CS_HASHTYPE_SHA256 = 2, diff --git a/dyld3/MachOAnalyzer.cpp b/dyld3/MachOAnalyzer.cpp index 27eaf0a..ec3b8d7 100644 --- a/dyld3/MachOAnalyzer.cpp +++ b/dyld3/MachOAnalyzer.cpp @@ -2881,7 +2881,7 @@ bool MachOAnalyzer::hasUnalignedPointerFixups() const } 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& cummulativeString, int curStrOffset, bool& stop, ExportsCallback callback) const { if ( p >= end ) { diag.error("malformed trie, node past end"); @@ -2907,7 +2907,7 @@ void MachOAnalyzer::recurseTrie(Diagnostics& diag, const uint8_t* const start, c } if ( diag.hasError() ) return; - callback(cummulativeString, imageOffset, flags, other, importName, stop); + callback(cummulativeString.begin(), imageOffset, flags, other, importName, stop); if ( stop ) return; } @@ -2920,6 +2920,7 @@ void MachOAnalyzer::recurseTrie(Diagnostics& diag, const uint8_t* const start, c 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 ) { @@ -2927,6 +2928,7 @@ void MachOAnalyzer::recurseTrie(Diagnostics& diag, const uint8_t* const start, c return; } } + cummulativeString.resize(curStrOffset+edgeStrLen + 1); cummulativeString[curStrOffset+edgeStrLen] = *s++; uint64_t childNodeOffset = read_uleb128(diag, s, end); if (childNodeOffset == 0) { @@ -2949,7 +2951,7 @@ void MachOAnalyzer::forEachExportedSymbol(Diagnostics& diag, ExportsCallback cal 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); } } @@ -3911,6 +3913,25 @@ uint32_t MachOAnalyzer::loadCommandsFreeSpace() const 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 diff --git a/dyld3/MachOAnalyzer.h b/dyld3/MachOAnalyzer.h index 6c47a96..757ab6e 100644 --- a/dyld3/MachOAnalyzer.h +++ b/dyld3/MachOAnalyzer.h @@ -292,6 +292,8 @@ struct VIS_HIDDEN MachOAnalyzer : public MachOLoaded bool hasObjCMessageReferences() const; const ObjCImageInfo* objcImageInfo() const; + + void forEachWeakDef(Diagnostics& diag, void (^handler)(const char* symbolName, uintptr_t imageOffset, bool isFromExportTrie)) const; private: @@ -341,7 +343,7 @@ 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& cummulativeString, int curStrOffset, bool& stop, MachOAnalyzer::ExportsCallback callback) const; void analyzeSegmentsLayout(uint64_t& vmSpace, bool& hasZeroFill) const; }; diff --git a/dyld3/shared-cache/CacheBuilder.cpp b/dyld3/shared-cache/CacheBuilder.cpp index 8ec3742..0666b0c 100644 --- a/dyld3/shared-cache/CacheBuilder.cpp +++ b/dyld3/shared-cache/CacheBuilder.cpp @@ -84,19 +84,19 @@ #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 } }; @@ -2530,10 +2530,12 @@ void CacheBuilder::codeSign() // 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; @@ -2595,7 +2597,7 @@ void CacheBuilder::codeSign() 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 @@ -2635,7 +2637,7 @@ void CacheBuilder::codeSign() 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 @@ -2673,25 +2675,25 @@ void CacheBuilder::codeSign() 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)); } }; @@ -2704,7 +2706,7 @@ void CacheBuilder::codeSign() { 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); diff --git a/dyld3/shared-cache/CacheBuilder.h b/dyld3/shared-cache/CacheBuilder.h index 18602fe..6d6ef41 100644 --- a/dyld3/shared-cache/CacheBuilder.h +++ b/dyld3/shared-cache/CacheBuilder.h @@ -190,6 +190,7 @@ private: uint64_t sharedRegionPadding; uint64_t pointerDeltaMask; const char* archName; + uint16_t csPageSize; uint8_t sharedRegionAlignP2; uint8_t slideInfoBytesPerPage; bool sharedRegionsAreDiscontiguous; diff --git a/launch-cache/dsc_extractor.cpp b/launch-cache/dsc_extractor.cpp index c46a4d6..95a5fa1 100644 --- a/launch-cache/dsc_extractor.cpp +++ b/launch-cache/dsc_extractor.cpp @@ -654,7 +654,6 @@ static int sharedCacheIsValid(const void* mapped_cache, uint64_t size) { 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; @@ -695,6 +694,8 @@ static int sharedCacheIsValid(const void* mapped_cache, uint64_t size) { 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; @@ -728,7 +729,7 @@ static int sharedCacheIsValid(const void* mapped_cache, uint64_t size) { 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 diff --git a/src/ImageLoader.cpp b/src/ImageLoader.cpp index 1723c06..839ab99 100644 --- a/src/ImageLoader.cpp +++ b/src/ImageLoader.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include @@ -968,12 +969,27 @@ static const char* const sTreatAsWeak[] = { "__ZdaPvSt11align_val_t", "__ZdaPvSt11align_val_tRKSt9nothrow_t", "__ZdaPvmSt11align_val_t" }; +size_t ImageLoader::HashCString::hash(const char* v) { + // FIXME: Use hash when it has the correct visibility markup + return std::hash{}(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]; @@ -989,6 +1005,298 @@ void ImageLoader::weakBind(const LinkContext& context) ++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, 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(); + // 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 @@ -1063,9 +1371,9 @@ void ImageLoader::weakBind(const LinkContext& context) 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) @@ -1073,7 +1381,7 @@ void ImageLoader::weakBind(const LinkContext& context) //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); @@ -1134,7 +1442,7 @@ void ImageLoader::weakBind(const LinkContext& context) } 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; } } } @@ -1180,7 +1488,7 @@ void ImageLoader::weakBind(const LinkContext& context) uint64_t t2 = mach_absolute_time(); fgTotalWeakBindTime += t2 - t1; - + if ( context.verboseWeakBind ) dyld::log("dyld: weak bind end\n"); } diff --git a/src/ImageLoader.h b/src/ImageLoader.h index a9aab03..1a3d415 100644 --- a/src/ImageLoader.h +++ b/src/ImageLoader.h @@ -52,6 +52,8 @@ #include "DyldSharedCache.h" +#include "Map.h" + #if __arm__ #include #endif @@ -242,6 +244,14 @@ public: }; 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); @@ -291,6 +301,10 @@ public: size_t dynamicInterposeCount; PrebindMode prebindUsage; SharedRegionMode sharedRegionMode; + mutable dyld3::Map, HashCString, EqualCString> weakDefMap; + mutable bool weakDefMapInitialized = false; + mutable bool weakDefMapProcessedLaunchDefs = false; + mutable bool useNewWeakBind = false; bool dyldLoadedAtSameAddressNeededBySharedCache; bool strictMachORequired; bool allowAtPaths; @@ -789,7 +803,7 @@ protected: // 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 @@ -852,6 +866,8 @@ private: void processInitializers(const LinkContext& context, mach_port_t this_thread, InitializerTimingList& timingInfo, ImageLoader::UninitedUpwards& ups); + void weakBindOld(const LinkContext& context); + recursive_lock* fInitializerRecursiveLock; union { diff --git a/src/ImageLoaderMegaDylib.cpp b/src/ImageLoaderMegaDylib.cpp index b14af26..71374b0 100644 --- a/src/ImageLoaderMegaDylib.cpp +++ b/src/ImageLoaderMegaDylib.cpp @@ -518,7 +518,7 @@ void ImageLoaderMegaDylib::appendImagesNeedingCoalescing(ImageLoader* images[], } -bool ImageLoaderMegaDylib::weakSymbolsBound(unsigned index) +bool ImageLoaderMegaDylib::weakSymbolsBound(unsigned index) const { return ( _stateFlags[index] >= kStateFlagWeakBound ); } diff --git a/src/ImageLoaderMegaDylib.h b/src/ImageLoaderMegaDylib.h index 5393ddf..081e95b 100644 --- a/src/ImageLoaderMegaDylib.h +++ b/src/ImageLoaderMegaDylib.h @@ -186,7 +186,7 @@ protected: 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: diff --git a/src/dyld2.cpp b/src/dyld2.cpp index 6ed2b38..8a438af 100644 --- a/src/dyld2.cpp +++ b/src/dyld2.cpp @@ -1470,6 +1470,25 @@ void removeImage(ImageLoader* image) } } + // 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(); diff --git a/testing/test-cases/bind-addend.dtest/main.c b/testing/test-cases/bind-addend.dtest/main.c new file mode 100644 index 0000000..a059637 --- /dev/null +++ b/testing/test-cases/bind-addend.dtest/main.c @@ -0,0 +1,40 @@ + +// 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 + +// 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; +} + diff --git a/testing/test-cases/rpath-weak-missing.dtest/foo.c b/testing/test-cases/rpath-weak-missing.dtest/foo.c new file mode 100644 index 0000000..723758f --- /dev/null +++ b/testing/test-cases/rpath-weak-missing.dtest/foo.c @@ -0,0 +1 @@ +int foo = 42; diff --git a/testing/test-cases/rpath-weak-missing.dtest/main.c b/testing/test-cases/rpath-weak-missing.dtest/main.c new file mode 100644 index 0000000..4ed1aa6 --- /dev/null +++ b/testing/test-cases/rpath-weak-missing.dtest/main.c @@ -0,0 +1,31 @@ + +// 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 +#include + + +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; +} + + diff --git a/testing/test-cases/weak-coalesce-unload.dtest/foo1.c b/testing/test-cases/weak-coalesce-unload.dtest/foo1.c new file mode 100644 index 0000000..4083aec --- /dev/null +++ b/testing/test-cases/weak-coalesce-unload.dtest/foo1.c @@ -0,0 +1,7 @@ + +__attribute__((weak)) +int foo = 1; + +void* fooPtr() { + return &foo; +} \ No newline at end of file diff --git a/testing/test-cases/weak-coalesce-unload.dtest/foo2.c b/testing/test-cases/weak-coalesce-unload.dtest/foo2.c new file mode 100644 index 0000000..81644b9 --- /dev/null +++ b/testing/test-cases/weak-coalesce-unload.dtest/foo2.c @@ -0,0 +1,11 @@ + +__attribute__((weak)) +int foo() { + return 2; +} + +void* fooPtr() { + return &foo; +} + + diff --git a/testing/test-cases/weak-coalesce-unload.dtest/foo3.c b/testing/test-cases/weak-coalesce-unload.dtest/foo3.c new file mode 100644 index 0000000..f09ee9d --- /dev/null +++ b/testing/test-cases/weak-coalesce-unload.dtest/foo3.c @@ -0,0 +1,11 @@ + +__attribute__((weak)) +int foo() { + return 3; +} + +void* fooPtr() { + return &foo; +} + + diff --git a/testing/test-cases/weak-coalesce-unload.dtest/main.c b/testing/test-cases/weak-coalesce-unload.dtest/main.c new file mode 100644 index 0000000..dc98c47 --- /dev/null +++ b/testing/test-cases/weak-coalesce-unload.dtest/main.c @@ -0,0 +1,127 @@ + +// 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 +#include +#include + +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; +} + -- 2.45.2