2 // BUILD: $CC linked1.m -dynamiclib -o $BUILD_DIR/liblinked1.dylib -install_name $RUN_DIR/liblinked1.dylib -lobjc
3 // BUILD: $CC linked2.m -dynamiclib -o $BUILD_DIR/liblinked2.dylib -install_name $RUN_DIR/liblinked2.dylib -lobjc
4 // BUILD: $CC main.m -o $BUILD_DIR/_dyld_for_each_objc_class-duplicates.exe $BUILD_DIR/liblinked1.dylib $BUILD_DIR/liblinked2.dylib -lobjc -framework Foundation
6 // RUN: ./_dyld_for_each_objc_class-duplicates.exe
8 #include <mach-o/dyld_priv.h>
12 #import <Foundation/NSObject.h>
14 #include "test_support.h"
16 // All the libraries have a copy of NSString
17 @interface NSString : NSObject
20 @implementation NSString
23 extern Class OBJC_CLASS_$_NSString;
25 // The main executable also has versions of these Foundation classes
26 @interface NSDictionary : NSObject
29 @implementation NSDictionary
32 extern Class OBJC_CLASS_$_NSDictionary;
34 @interface NSError : NSObject
37 @implementation NSError
40 extern Class OBJC_CLASS_$_NSError;
42 @interface NSSet : NSObject
48 extern Class OBJC_CLASS_$_NSSet;
50 @interface NSArray : NSObject
53 @implementation NSArray
56 extern Class OBJC_CLASS_$_NSArray;
58 Class getMainNSString() {
59 return (Class)&OBJC_CLASS_$_NSString;
62 extern id objc_getClass(const char *name);
64 // Get the NSString from liblinked1.dylib
65 extern Class getLinked1NSString();
67 // Get the NSString from liblinked2.dylib
68 extern Class getLinked2NSString();
70 static bool gotNSStringMain = false;
71 static bool gotNSStringLinked = false;
72 static bool gotNSStringLinked2 = false;
73 static bool gotNSStringFoundation = false;
75 void testDuplicate(const char* className, Class nonCacheClass) {
76 // Walk all the implementations of the class. There should be 2. One in the executable and one in the shared cache
77 // The shared cache one should be returned first.
79 // The objc runtime should have chosen the Foundation one as the canonical definition.
80 Class objcRuntimeClassImpl = (Class)objc_getClass(className);
81 if (objcRuntimeClassImpl == nil) {
82 FAIL("class %s not found via runtime", className);
85 if (objcRuntimeClassImpl == nonCacheClass) {
86 FAIL("class %s from runtime should not match main exexutable", className);
89 __block bool foundSharedCacheImpl = false;
90 __block bool foundMainExecutableImpl = false;
91 __block bool foundAnyClass = false;
92 __block bool foundTooManyClasses = false;
93 _dyld_for_each_objc_class(className, ^(void* classPtr, bool isLoaded, bool* stop) {
96 // We should walk these in the order Foundation, main exe
97 if (!foundSharedCacheImpl) {
98 if (classPtr != objcRuntimeClassImpl) {
99 FAIL("Optimized class %s should have come from Foundation", className);
102 FAIL("Optimized class %s isLoaded should have been set on Foundation", className);
104 foundSharedCacheImpl = true;
107 if (!foundMainExecutableImpl) {
108 if (classPtr != nonCacheClass) {
109 FAIL("Optimized class %s should have come from main executable", className);
112 FAIL("Optimized class %s isLoaded should have been set on main executable", className);
114 foundMainExecutableImpl = true;
118 foundTooManyClasses = true;
119 FAIL("class %s found somewhere other than main executable and Foundation", className);
122 if (!foundAnyClass) {
123 FAIL("class %s not found", className);
126 if (foundTooManyClasses) {
127 FAIL("class %s found too many times", className);
130 if (!foundSharedCacheImpl || !foundMainExecutableImpl) {
131 FAIL("class %s not found for shared cache or main executable", className);
135 int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
136 // This API is only available with dyld3 and shared caches. If we have dyld2 then don't do anything
137 const char* testDyldMode = getenv("TEST_DYLD_MODE");
138 assert(testDyldMode);
140 size_t sharedCacheLen = 0;
141 const void* sharedCacheStart = 0;
142 sharedCacheStart = _dyld_get_shared_cache_range(&sharedCacheLen);
143 bool haveSharedCache = sharedCacheStart != NULL;
144 if (!strcmp(testDyldMode, "2") || !haveSharedCache) {
145 __block bool sawClass = false;
146 _dyld_for_each_objc_class("NSString", ^(void* classPtr, bool isLoaded, bool* stop) {
150 FAIL("dyld2 shouldn't see any classes");
152 PASS("dyld2 or no shared cache)");
155 // Check that NSString comes from Foundation as the shared cache should win here.
156 id runtimeNSString = objc_getClass("NSString");
157 if ( (uint64_t)runtimeNSString < (uint64_t)sharedCacheStart ) {
158 FAIL("NSString should have come from Foundation but instead was %p", runtimeNSString);
160 if ( (uint64_t)runtimeNSString >= ((uint64_t)sharedCacheStart + sharedCacheLen) ) {
161 FAIL("NSString should have come from Foundation but instead was %p", runtimeNSString);
164 // Walk all the implementations of "NSString"
165 _dyld_for_each_objc_class("NSString", ^(void* classPtr, bool isLoaded, bool* stop) {
166 // We should walk these in the order Foundation, liblinked2, liblinked, main exe
167 if (!gotNSStringFoundation) {
168 if (classPtr != runtimeNSString) {
169 FAIL("Optimized NSString should have come from Foundation");
172 FAIL("Optimized NSString isLoaded should have been set on Foundation");
174 gotNSStringFoundation = true;
177 if (!gotNSStringLinked2) {
178 if (classPtr != getLinked2NSString()) {
179 FAIL("Optimized NSString should have come from liblinked2");
182 FAIL("Optimized NSString isLoaded should have been set on liblinked2");
184 gotNSStringLinked2 = true;
187 if (!gotNSStringLinked) {
188 if (classPtr != getLinked1NSString()) {
189 FAIL("Optimized NSString should have come from liblinked");
192 FAIL("Optimized NSString isLoaded should have been set on liblinked");
194 gotNSStringLinked = true;
197 if (!gotNSStringMain) {
198 if (classPtr != getMainNSString()) {
199 FAIL("Optimized NSString should have come from main exe");
202 FAIL("Optimized NSString isLoaded should have been set on main exe");
204 gotNSStringMain = true;
207 FAIL("Unexpected Optimized NSString");
210 if ( !gotNSStringFoundation || !gotNSStringLinked2 || !gotNSStringLinked || !gotNSStringMain) {
211 FAIL("Failed to find all duplicates of 'NSString'");
214 // Visit again, and return Foundation's NSString
215 __block void* NSStringImpl = nil;
216 _dyld_for_each_objc_class("NSString", ^(void* classPtr, bool isLoaded, bool* stop) {
217 NSStringImpl = classPtr;
220 if (NSStringImpl != runtimeNSString) {
221 FAIL("_dyld_for_each_objc_class should have returned NSString from Foundation");
224 testDuplicate("NSDictionary", (Class)&OBJC_CLASS_$_NSDictionary);
225 testDuplicate("NSError", (Class)&OBJC_CLASS_$_NSError);
226 testDuplicate("NSSet", (Class)&OBJC_CLASS_$_NSSet);
227 testDuplicate("NSArray", (Class)&OBJC_CLASS_$_NSArray);