]> git.saurik.com Git - apple/dyld.git/blob - testing/test-cases/_dyld_for_each_objc_class-duplicates.dtest/main.m
dyld-733.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 }