3 // BUILD: $CC missing.m -dynamiclib -o $TEMP_DIR/libmissing.dylib -install_name $BUILD_DIR/libmissing.dylib -lobjc -Wl,-fixup_chains
4 // BUILD: $CC lib1.m -dynamiclib -o $BUILD_DIR/liblinked1.dylib -install_name $RUN_DIR/liblinked1.dylib -lobjc -Wl,-fixup_chains
5 // BUILD: $CC lib2.m -dynamiclib -o $BUILD_DIR/liblinked2.dylib -install_name $RUN_DIR/liblinked2.dylib -lobjc $TEMP_DIR/libmissing.dylib -Wl,-fixup_chains
6 // BUILD: $CC main.mm -o $BUILD_DIR/_dyld_for_each_objc_class-missing-weak-chained.exe -lobjc $BUILD_DIR/liblinked1.dylib $BUILD_DIR/liblinked2.dylib $TEMP_DIR/libmissing.dylib -Wl,-fixup_chains -lc++
8 // RUN: ./_dyld_for_each_objc_class-missing-weak-chained.exe
10 // liblinked2 weakly links libmissing and so has a missing weak superclass.
11 // This means we should not see classes from liblinked be returned from _dyld_for_each_objc_class
12 // liblinked1 itself has classes which are fine so shoud be in the map.
13 // At runtime, objc is going to walk the images in reverse load order so will see the classes in liblinked2 first
14 // which are the ones we couldn't optimize. But objc should then check the closure class map and choose the class
17 #include <mach-o/dyld_priv.h>
19 #import <Foundation/Foundation.h>
21 // All the libraries have a copy of DyldClass
22 @interface DyldClass : NSObject
25 @implementation DyldClass
28 // Only the main executable has DyldMainClass
29 @interface DyldMainClass : NSObject
32 @implementation DyldMainClass
35 extern Class OBJC_CLASS_$_DyldClass;
36 extern Class OBJC_CLASS_$_DyldMainClass;
38 Class getMainDyldClass() {
39 return (Class)&OBJC_CLASS_$_DyldClass;
42 Class getMainDyldMainClass() {
43 return (Class)&OBJC_CLASS_$_DyldMainClass;
46 extern "C" int printf(const char*, ...);
48 extern "C" id objc_getClass(const char *name);
50 // Get the DyldClass from liblinked1.dylib
51 extern "C" id getLinked1DyldClass();
53 // Get the DyldLinkedClass from liblinked1.dylib
54 extern "C" id getLinked1DyldLinkedClass();
56 // Get the DyldLinkedClass from liblinked.dylib
57 extern "C" id getLinked2DyldLinkedClass();
59 // Get the DyldClass from libmissing.dylib
60 // Note, this is weak_import and missing so this must fail
61 __attribute__((weak_import))
62 extern "C" id getMissingDyldClass();
64 static bool gotDyldClassMain = false;
65 static bool gotDyldClassLinked1 = false;
68 printf("[BEGIN] _dyld_for_each_objc_class-missing-weak-chained\n");
70 // This API is only available with dyld3 and shared caches. If we have dyld2 then don't do anything
71 const char* testDyldMode = getenv("TEST_DYLD_MODE");
74 size_t unusedCacheLen;
75 bool haveSharedCache = _dyld_get_shared_cache_range(&unusedCacheLen) != 0;
76 if (!strcmp(testDyldMode, "2") || !haveSharedCache) {
77 __block bool sawClass = false;
78 _dyld_for_each_objc_class("DyldClass", ^(void* classPtr, bool isLoaded, bool* stop) {
82 printf("[FAIL] _dyld_for_each_objc_class: dyld2 shouldn't see any classes\n");
85 printf("[PASS] _dyld_for_each_objc_class (dyld2 or no shared cache)\n");
89 // Make sure libmissing.dylib is actually missing
90 if (&getMissingDyldClass != nil) {
91 printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: libmissing needs to be missing\n");
95 // DyldClass in liblinked1 should exist as its superclass is just NSObject
96 if (getLinked1DyldClass() == nil) {
97 printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: liblinked1 DyldClass should exist\n");
101 // DyldLinkedClass in liblinked1 should exist as its superclass is just NSObject
102 if (getLinked1DyldLinkedClass() == nil) {
103 printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: liblinked1 DyldLinkedClass should exist\n");
107 // DyldLinkedClass in liblinked2 should exist as its superclass is just NSObject
108 if (getLinked2DyldLinkedClass() == nil) {
109 printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: liblinked2 DyldLinkedClass should exist\n");
113 // Check that DyldMainClass comes main.exe as that is its only definition
114 id runtimeDyldMainClass = objc_getClass("DyldMainClass");
115 if (runtimeDyldMainClass != getMainDyldMainClass()) {
116 printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: DyldMainClass should have come from main.exe\n");
120 // Check that DyldClass comes liblinked1 as it should be missing from liblinked2
121 id runtimeDyldClass = objc_getClass("DyldClass");
122 if (runtimeDyldClass != getLinked1DyldClass()) {
123 printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: DyldClass should have come from liblinked1\n");
127 // Check that DyldLinkedClass comes from liblinked2
128 // Note, this changes once the objc runtime has adopted our changes. Don't test it for now
130 id runtimeDyldLinkedClass = objc_getClass("DyldLinkedClass");
131 if (runtimeDyldLinkedClass != getLinked2DyldLinkedClass()) {
132 printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: DyldLinkedClass should have come from liblinked2\n");
137 _dyld_for_each_objc_class("DyldClass", ^(void* classPtr, bool isLoaded, bool* stop) {
138 // We should walk these in the order liblinked, main exe
139 if (!gotDyldClassLinked1) {
140 if (classPtr != getLinked1DyldClass()) {
141 printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: DyldClass should have come from liblinked1\n");
146 printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: DyldClass isLoaded should have been set on liblinked1\n");
150 gotDyldClassLinked1 = true;
153 if (!gotDyldClassMain) {
154 if (classPtr != getMainDyldClass()) {
155 printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: DyldClass should have come from main exe\n");
160 printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: DyldClass isLoaded should have been set on main exe\n");
164 gotDyldClassMain = true;
167 printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: Unexpected DyldClass\n");
171 if (!gotDyldClassLinked1) {
172 printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: _dyld_for_each_objc_class should have seen DyldClass in liblinked1\n");
176 if (!gotDyldClassMain) {
177 printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: _dyld_for_each_objc_class should have seen DyldClass in main.exe\n");
181 // Visit again, and return liblinked1's DyldClass
182 // Visit again, and return liblinked2's DyldClass
183 __block void* dyldClassImpl = nil;
184 _dyld_for_each_objc_class("DyldClass", ^(void* classPtr, bool isLoaded, bool* stop) {
185 dyldClassImpl = classPtr;
188 if (dyldClassImpl != getLinked1DyldClass()) {
189 printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: _dyld_for_each_objc_class should have returned DyldClass from liblinked1\n");
193 // Visit again, and return liblinked1's DyldClass
195 _dyld_for_each_objc_class("DyldClass", ^(void* classPtr, bool isLoaded, bool* stop) {
196 // We should walk these in the order liblinked, main exe
197 // And return the one from main.exe
198 if (classPtr == getLinked1DyldClass())
200 dyldClassImpl = classPtr;
203 if (dyldClassImpl != getMainDyldClass()) {
204 printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: _dyld_for_each_objc_class should have returned DyldClass from main.exe\n");
208 printf("[PASS] _dyld_for_each_objc_class-missing-weak-chained\n");