]> git.saurik.com Git - apple/dyld.git/commitdiff
dyld-733.6.tar.gz macos-10151 v733.6
authorApple <opensource@apple.com>
Tue, 24 Mar 2020 21:12:03 +0000 (21:12 +0000)
committerApple <opensource@apple.com>
Tue, 24 Mar 2020 21:12:03 +0000 (21:12 +0000)
20 files changed:
dyld3/Closure.h
dyld3/ClosureBuilder.cpp
dyld3/CodeSigningTypes.h
dyld3/MachOAnalyzer.cpp
dyld3/MachOAnalyzer.h
dyld3/shared-cache/CacheBuilder.cpp
dyld3/shared-cache/CacheBuilder.h
launch-cache/dsc_extractor.cpp
src/ImageLoader.cpp
src/ImageLoader.h
src/ImageLoaderMegaDylib.cpp
src/ImageLoaderMegaDylib.h
src/dyld2.cpp
testing/test-cases/bind-addend.dtest/main.c [new file with mode: 0644]
testing/test-cases/rpath-weak-missing.dtest/foo.c [new file with mode: 0644]
testing/test-cases/rpath-weak-missing.dtest/main.c [new file with mode: 0644]
testing/test-cases/weak-coalesce-unload.dtest/foo1.c [new file with mode: 0644]
testing/test-cases/weak-coalesce-unload.dtest/foo2.c [new file with mode: 0644]
testing/test-cases/weak-coalesce-unload.dtest/foo3.c [new file with mode: 0644]
testing/test-cases/weak-coalesce-unload.dtest/main.c [new file with mode: 0644]

index 20d5c0be4472b2a62372e9a7b92bc507757a7a64..4b4eb39f99a3b0457b7029bd495a722aff66227c 100644 (file)
@@ -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
index f4827f97ce0d258476762ce5582ba711be7b35b8..daef7525b68c079c8bdbbb82379f4f16574a0e2e 100644 (file)
@@ -672,6 +672,10 @@ void ClosureBuilder::recursiveLoadDependents(LoadedImageChain& forImageChain, bo
         }
         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);
@@ -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;
     }
index 22af412b253eda30cc17ffa729ceddf190e45d3c..17b4a8099fc79ab8e9dd886df26dc77b5a295fd7 100644 (file)
@@ -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,
index 27eaf0a607ad310dba2bfd4604d5438abf3a1b0f..ec3b8d7ab2f3aed6ba87855081a8ce6c20a97a5f 100644 (file)
@@ -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<char>& 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
 
index 6c47a96d1e87d4aa42077a2d5971706c3804f863..757ab6eb70c84057239d582fa0dd2f69d99168fe 100644 (file)
@@ -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<char>& cummulativeString, int curStrOffset, bool& stop, MachOAnalyzer::ExportsCallback callback) const;
     void                    analyzeSegmentsLayout(uint64_t& vmSpace, bool& hasZeroFill) const;
 
 };
index 8ec374247b6b7bab1c6be02e02a67f0545efa96f..0666b0c0a2a1183300e5849912cc32fd1f28f38e 100644 (file)
 #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);
index 18602feb3b415bc53942f1bcd8ce258f3284904f..6d6ef41c7605a8a6e2f65cd1b9ec64626ca42922 100644 (file)
@@ -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;
index c46a4d69b6d071538e8f67c6a4ce2a227c42e084..95a5fa1a8b9987311433c0ff3789d656ffc5aa0f 100644 (file)
@@ -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
index 1723c0677d3abe76348508bd506cb8216edf395b..839ab99436e00dac5229c2dd95ee8feda5b7f07b 100644 (file)
@@ -36,6 +36,7 @@
 #include <sys/mount.h>
 #include <sys/sysctl.h>
 #include <libkern/OSAtomic.h>
+#include <string_view>
 
 #include <atomic>
 
@@ -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<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];
@@ -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<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
@@ -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");
 }
index a9aab035a8d3d8e8e06e563c0b6ed3ddb43d353c..1a3d41579d9d13a8c7b64b165e7e13d5bdb32017 100644 (file)
@@ -52,6 +52,8 @@
 
 #include "DyldSharedCache.h"
 
+#include "Map.h"
+
 #if __arm__
  #include <mach/vm_page_size.h>
 #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<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;
@@ -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 {
index b14af260f1ca9b867c9d6d5ed2f09a11215ad6da..71374b0349254e83c1e783b1b70c8ca07f6ace42 100644 (file)
@@ -518,7 +518,7 @@ void ImageLoaderMegaDylib::appendImagesNeedingCoalescing(ImageLoader* images[],
 }
 
 
-bool ImageLoaderMegaDylib::weakSymbolsBound(unsigned index)
+bool ImageLoaderMegaDylib::weakSymbolsBound(unsigned index) const
 {
        return ( _stateFlags[index] >= kStateFlagWeakBound );
 }
index 5393ddf95fce5e7c0f646184f62058f3bf88e1ec..081e95b1be5a398d7ca3e352535d027d5855b8f5 100644 (file)
@@ -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:
index 6ed2b38583d36d0e77e4e3c1723a5324bae2c31f..8a438afcfce80b7974f201c1c91ab576a436f179 100644 (file)
@@ -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 (file)
index 0000000..a059637
--- /dev/null
@@ -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 <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;
+}
+
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 (file)
index 0000000..723758f
--- /dev/null
@@ -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 (file)
index 0000000..4ed1aa6
--- /dev/null
@@ -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 <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;
+}
+
+
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 (file)
index 0000000..4083aec
--- /dev/null
@@ -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 (file)
index 0000000..81644b9
--- /dev/null
@@ -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 (file)
index 0000000..f09ee9d
--- /dev/null
@@ -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 (file)
index 0000000..dc98c47
--- /dev/null
@@ -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 <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;
+}
+