dyld-732.8.tar.gz
[apple/dyld.git] / testing / test-cases / _dyld_for_each_objc_class-duplicates.dtest / main.m
1
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
5
6 // RUN:  ./_dyld_for_each_objc_class-duplicates.exe
7
8 #include <mach-o/dyld_priv.h>
9 #include <stdlib.h>
10 #include <string.h>
11
12 #import <Foundation/NSObject.h>
13
14 // All the libraries have a copy of NSString
15 @interface NSString : NSObject
16 @end
17
18 @implementation NSString
19 @end
20
21 extern Class OBJC_CLASS_$_NSString;
22
23 // The main executable also has versions of these Foundation classes
24 @interface NSDictionary : NSObject
25 @end
26
27 @implementation NSDictionary
28 @end
29
30 extern Class OBJC_CLASS_$_NSDictionary;
31
32 @interface NSError : NSObject
33 @end
34
35 @implementation NSError
36 @end
37
38 extern Class OBJC_CLASS_$_NSError;
39
40 @interface NSSet : NSObject
41 @end
42
43 @implementation NSSet
44 @end
45
46 extern Class OBJC_CLASS_$_NSSet;
47
48 @interface NSArray : NSObject
49 @end
50
51 @implementation NSArray
52 @end
53
54 extern Class OBJC_CLASS_$_NSArray;
55
56 Class getMainNSString() {
57   return (Class)&OBJC_CLASS_$_NSString;
58 }
59
60 extern int printf(const char*, ...);
61
62 extern id objc_getClass(const char *name);
63
64 // Get the NSString from liblinked1.dylib
65 extern Class getLinked1NSString();
66
67 // Get the NSString from liblinked2.dylib
68 extern Class getLinked2NSString();
69
70 static bool gotNSStringMain = false;
71 static bool gotNSStringLinked = false;
72 static bool gotNSStringLinked2 = false;
73 static bool gotNSStringFoundation = false;
74
75 bool 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.
78
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     printf("[FAIL] _dyld_for_each_objc_class-duplicates: class %s not found via runtime\n", className);
83     return false;
84   }
85
86   if (objcRuntimeClassImpl == nonCacheClass) {
87     printf("[FAIL] _dyld_for_each_objc_class-duplicates: class %s from runtime should not match main exexutable\n", className);
88     return false;
89   }
90
91   __block bool foundSharedCacheImpl = false;
92   __block bool foundMainExecutableImpl = false;
93   __block bool foundAnyClass = false;
94   __block bool foundTooManyClasses = false;
95   _dyld_for_each_objc_class(className, ^(void* classPtr, bool isLoaded, bool* stop) {
96     foundAnyClass = true;
97
98     // We should walk these in the order Foundation, main exe
99     if (!foundSharedCacheImpl) {
100       if (classPtr != objcRuntimeClassImpl) {
101         printf("[FAIL] _dyld_for_each_objc_class-duplicates: Optimized class %s should have come from Foundation\n", className);
102         *stop = true;
103         return;
104       }
105       if (!isLoaded) {
106         printf("[FAIL] _dyld_for_each_objc_class-duplicates: Optimized class %s isLoaded should have been set on Foundation\n", className);
107         *stop = true;
108         return;
109       }
110       foundSharedCacheImpl = true;
111       return;
112     }
113     if (!foundMainExecutableImpl) {
114       if (classPtr != nonCacheClass) {
115         printf("[FAIL] _dyld_for_each_objc_class-duplicates: Optimized class %s should have come from main executable\n", className);
116         *stop = true;
117         return;
118       }
119       if (!isLoaded) {
120         printf("[FAIL] _dyld_for_each_objc_class-duplicates: Optimized class %s isLoaded should have been set on main executable\n", className);
121         *stop = true;
122         return;
123       }
124       foundMainExecutableImpl = true;
125       return;
126     }
127
128     foundTooManyClasses = true;
129     printf("[FAIL] _dyld_for_each_objc_class-duplicates: class %s found somewhere other than main executable and Foundation\n", className);
130     *stop = true;
131     return;
132   });
133
134   if (!foundAnyClass) {
135     printf("[FAIL] _dyld_for_each_objc_class-duplicates: class %s not found\n", className);
136     return false;
137   }
138
139   if (foundTooManyClasses) {
140     printf("[FAIL] _dyld_for_each_objc_class-duplicates: class %s found too many times\n", className);
141     return false;
142   }
143
144   if (!foundSharedCacheImpl || !foundMainExecutableImpl) {
145     printf("[FAIL] _dyld_for_each_objc_class-duplicates: class %s not found for shared cache or main executable\n", className);
146     return false;
147   }
148
149   return true;
150 }
151
152 int main() {
153   printf("[BEGIN] _dyld_for_each_objc_class-duplicates\n");
154
155   // This API is only available with dyld3 and shared caches.  If we have dyld2 then don't do anything
156   const char* testDyldMode = getenv("TEST_DYLD_MODE");
157   assert(testDyldMode);
158
159   size_t sharedCacheLen = 0;
160   const void* sharedCacheStart = 0;
161   sharedCacheStart = _dyld_get_shared_cache_range(&sharedCacheLen);
162   bool haveSharedCache = sharedCacheStart != NULL;
163   if (!strcmp(testDyldMode, "2") || !haveSharedCache) {
164     __block bool sawClass = false;
165     _dyld_for_each_objc_class("NSString", ^(void* classPtr, bool isLoaded, bool* stop) {
166       sawClass = true;
167     });
168     if (sawClass) {
169       printf("[FAIL] _dyld_for_each_objc_class-duplicates: dyld2 shouldn't see any classes\n");
170       return 0;
171     }
172     printf("[PASS] _dyld_for_each_objc_class-duplicates (dyld2 or no shared cache)\n");
173     return 0;
174   }
175
176   // Check that NSString comes from Foundation as the shared cache should win here.
177   id runtimeNSString = objc_getClass("NSString");
178   if ( (uint64_t)runtimeNSString < (uint64_t)sharedCacheStart ) {
179     printf("[FAIL] _dyld_for_each_objc_class-duplicates: NSString should have come from Foundation but instead was %p\n", runtimeNSString);
180     return 0;
181   }
182   if ( (uint64_t)runtimeNSString >= ((uint64_t)sharedCacheStart + sharedCacheLen) ) {
183     printf("[FAIL] _dyld_for_each_objc_class-duplicates: NSString should have come from Foundation but instead was %p\n", runtimeNSString);
184     return 0;
185   }
186
187   // Walk all the implementations of "NSString"
188   _dyld_for_each_objc_class("NSString", ^(void* classPtr, bool isLoaded, bool* stop) {
189     // We should walk these in the order Foundation, liblinked2, liblinked, main exe
190     if (!gotNSStringFoundation) {
191       if (classPtr != runtimeNSString) {
192         printf("[FAIL] _dyld_for_each_objc_class-duplicates: Optimized NSString should have come from Foundation\n");
193         *stop = true;
194         return;
195       }
196       if (!isLoaded) {
197         printf("[FAIL] _dyld_for_each_objc_class-duplicates: Optimized NSString isLoaded should have been set on Foundation\n");
198         *stop = true;
199         return;
200       }
201       gotNSStringFoundation = true;
202       return;
203     }
204     if (!gotNSStringLinked2) {
205       if (classPtr != getLinked2NSString()) {
206         printf("[FAIL] _dyld_for_each_objc_class-duplicates: Optimized NSString should have come from liblinked2\n");
207         *stop = true;
208         return;
209       }
210       if (!isLoaded) {
211         printf("[FAIL] _dyld_for_each_objc_class-duplicates: Optimized NSString isLoaded should have been set on liblinked2\n");
212         *stop = true;
213         return;
214       }
215       gotNSStringLinked2 = true;
216       return;
217     }
218     if (!gotNSStringLinked) {
219       if (classPtr != getLinked1NSString()) {
220         printf("[FAIL] _dyld_for_each_objc_class-duplicates: Optimized NSString should have come from liblinked\n");
221         *stop = true;
222         return;
223       }
224       if (!isLoaded) {
225         printf("[FAIL] _dyld_for_each_objc_class-duplicates: Optimized NSString isLoaded should have been set on liblinked\n");
226         *stop = true;
227         return;
228       }
229       gotNSStringLinked = true;
230       return;
231     }
232     if (!gotNSStringMain) {
233       if (classPtr != getMainNSString()) {
234         printf("[FAIL] _dyld_for_each_objc_class-duplicates: Optimized NSString should have come from main exe\n");
235         *stop = true;
236         return;
237       }
238       if (!isLoaded) {
239         printf("[FAIL] _dyld_for_each_objc_class-duplicates: Optimized NSString isLoaded should have been set on main exe\n");
240         *stop = true;
241         return;
242       }
243       gotNSStringMain = true;
244       return;
245     }
246     printf("[FAIL] _dyld_for_each_objc_class-duplicates: Unexpected Optimized NSString\n");
247     return;
248   });
249
250   if ( !gotNSStringFoundation || !gotNSStringLinked2 || !gotNSStringLinked || !gotNSStringMain) {
251     printf("[FAIL] _dyld_for_each_objc_class-duplicates: Failed to find all duplicates of 'NSString'\n");
252     return 0;
253   }
254
255   // Visit again, and return Foundation's NSString
256   __block void* NSStringImpl = nil;
257   _dyld_for_each_objc_class("NSString", ^(void* classPtr, bool isLoaded, bool* stop) {
258     NSStringImpl = classPtr;
259     *stop = true;
260   });
261   if (NSStringImpl != runtimeNSString) {
262     printf("[FAIL] _dyld_for_each_objc_class-duplicates: _dyld_for_each_objc_class should have returned NSString from Foundation\n");
263     return 0;
264   }
265
266   if (!testDuplicate("NSDictionary", (Class)&OBJC_CLASS_$_NSDictionary))
267     return 0;
268
269   if (!testDuplicate("NSError", (Class)&OBJC_CLASS_$_NSError))
270     return 0;
271
272   if (!testDuplicate("NSSet", (Class)&OBJC_CLASS_$_NSSet))
273     return 0;
274
275   if (!testDuplicate("NSArray", (Class)&OBJC_CLASS_$_NSArray))
276     return 0;
277
278   printf("[PASS] _dyld_for_each_objc_class-duplicates\n");
279
280   return 0;
281 }