1/* 2TEST_BUILD 3 $C{COMPILE} $DIR/unload4.m -o unload4.dylib -dynamiclib 4 $C{COMPILE_C} $DIR/unload3.c -o unload3.dylib -dynamiclib 5 $C{COMPILE} $DIR/unload2.m -o unload2.bundle -bundle 6 $C{COMPILE} $DIR/unload.m -o unload.out 7END 8 */ 9 10#include "test.h" 11#include <objc/runtime.h> 12#include <dlfcn.h> 13#include <unistd.h> 14 15#include "unload.h" 16 17#if __has_feature(objc_arc) 18 19int main() 20{ 21 testwarn("rdar://11368528 confused by Foundation"); 22 succeed(__FILE__); 23} 24 25#else 26 27static id forward_handler(void) 28{ 29 return 0; 30} 31 32static BOOL hasName(const char * const *names, const char *query) 33{ 34 const char *name; 35 while ((name = *names++)) { 36 if (strstr(name, query)) return YES; 37 } 38 39 return NO; 40} 41 42void cycle(void) 43{ 44 int i; 45 char buf[100]; 46 unsigned int imageCount, imageCount0; 47 const char **names; 48 const char *name; 49 50 names = objc_copyImageNames(&imageCount0); 51 testassert(names); 52 free(names); 53 54 void *bundle = dlopen("unload2.bundle", RTLD_LAZY); 55 testassert(bundle); 56 57 names = objc_copyImageNames(&imageCount); 58 testassert(names); 59 testassert(imageCount == imageCount0 + 1); 60 testassert(hasName(names, "unload2.bundle")); 61 free(names); 62 63 Class small = objc_getClass("SmallClass"); 64 Class big = objc_getClass("BigClass"); 65 testassert(small); 66 testassert(big); 67 68 name = class_getImageName(small); 69 testassert(name); 70 testassert(strstr(name, "unload2.bundle")); 71 name = class_getImageName(big); 72 testassert(name); 73 testassert(strstr(name, "unload2.bundle")); 74 75 id o1 = [small new]; 76 id o2 = [big new]; 77 testassert(o1); 78 testassert(o2); 79 80 // give BigClass and BigClass->isa large method caches (4692641) 81 for (i = 0; i < 10000; i++) { 82 sprintf(buf, "method_%d", i); 83 SEL sel = sel_registerName(buf); 84 ((void(*)(id, SEL))objc_msgSend)(o2, sel); 85 ((void(*)(id, SEL))objc_msgSend)(object_getClass(o2), sel); 86 } 87 88 RELEASE_VAR(o1); 89 RELEASE_VAR(o2); 90 91 testcollect(); 92 93 int err = dlclose(bundle); 94 testassert(err == 0); 95 err = dlclose(bundle); 96 testassert(err == -1); // already closed 97 98 testassert(objc_getClass("SmallClass") == NULL); 99 testassert(objc_getClass("BigClass") == NULL); 100 101 names = objc_copyImageNames(&imageCount); 102 testassert(names); 103 testassert(imageCount == imageCount0); 104 testassert(! hasName(names, "unload2.bundle")); 105 free(names); 106 107 // these selectors came from the bundle 108 testassert(0 == strcmp("unload2_instance_method", sel_getName(sel_registerName("unload2_instance_method")))); 109 testassert(0 == strcmp("unload2_category_method", sel_getName(sel_registerName("unload2_category_method")))); 110} 111 112int main() 113{ 114 // fixme object_dispose() not aggressive enough? 115 if (objc_collectingEnabled()) succeed(__FILE__); 116 117 objc_setForwardHandler((void*)&forward_handler, (void*)&forward_handler); 118 119#if defined(__arm__) 120 int count = 10; 121#else 122 int count = is_guardmalloc() ? 10 : 100; 123#endif 124 125 cycle(); 126#if __LP64__ 127 // fixme heap use goes up 512 bytes after the 2nd cycle only - bad or not? 128 cycle(); 129#endif 130 131 leak_mark(); 132 while (count--) { 133 cycle(); 134 } 135 leak_check(0); 136 137 // 5359412 Make sure dylibs with nothing other than image_info can close 138 void *dylib = dlopen("unload3.dylib", RTLD_LAZY); 139 testassert(dylib); 140 int err = dlclose(dylib); 141 testassert(err == 0); 142 err = dlclose(dylib); 143 testassert(err == -1); // already closed 144 145 // Make sure dylibs with real objc content cannot close 146 dylib = dlopen("unload4.dylib", RTLD_LAZY); 147 testassert(dylib); 148 err = dlclose(dylib); 149 testassert(err == 0); 150 err = dlclose(dylib); 151 testassert(err == 0); // dlopen from libobjc itself 152 err = dlclose(dylib); 153 testassert(err == -1); // already closed 154 155 succeed(__FILE__); 156} 157 158#endif 159