]> git.saurik.com Git - apple/objc4.git/blob - test/unload.m
objc4-818.2.tar.gz
[apple/objc4.git] / test / unload.m
1 // xpc leaks memory in dlopen(). Disable it.
2 // TEST_ENV XPC_SERVICES_UNAVAILABLE=1
3 /*
4 TEST_BUILD
5 $C{COMPILE} $DIR/unload4.m -o unload4.dylib -dynamiclib
6 $C{COMPILE_C} $DIR/unload3.c -o unload3.dylib -dynamiclib
7 $C{COMPILE} $DIR/unload2.m -o unload2.bundle -bundle $C{FORCE_LOAD_ARCLITE} -Xlinker -undefined -Xlinker dynamic_lookup
8 $C{COMPILE} $DIR/unload.m -o unload.exe -framework Foundation
9 END
10 */
11
12 /*
13 TEST_BUILD_OUTPUT
14 ld: warning: -undefined dynamic_lookup is deprecated on .*
15 OR
16 END
17 */
18
19 #include "test.h"
20 #include <objc/runtime.h>
21 #include <dlfcn.h>
22 #include <unistd.h>
23
24 #include "unload.h"
25
26 #if __has_feature(objc_arc)
27
28 int main()
29 {
30 testwarn("rdar://11368528 confused by Foundation");
31 succeed(__FILE__);
32 }
33
34 #else
35
36 static id forward_handler(void)
37 {
38 return 0;
39 }
40
41 static BOOL hasName(const char * const *names, const char *query)
42 {
43 const char *name;
44 while ((name = *names++)) {
45 if (strstr(name, query)) return YES;
46 }
47
48 return NO;
49 }
50
51 void cycle(void)
52 {
53 int i;
54 char buf[100];
55 unsigned int imageCount, imageCount0;
56 const char **names;
57 const char *name;
58
59 names = objc_copyImageNames(&imageCount0);
60 testassert(names);
61 free(names);
62
63 void *bundle = dlopen("unload2.bundle", RTLD_LAZY);
64 testassert(bundle);
65
66 names = objc_copyImageNames(&imageCount);
67 testassert(names);
68 testassert(imageCount == imageCount0 + 1);
69 testassert(hasName(names, "unload2.bundle"));
70 free(names);
71
72 Class small = objc_getClass("SmallClass");
73 Class big = objc_getClass("BigClass");
74 Class missing = objc_getClass("SubclassOfMissingWeakImport");
75 testassert(small);
76 testassert(big);
77 testassert(!missing);
78
79 name = class_getImageName(small);
80 testassert(name);
81 testassert(strstr(name, "unload2.bundle"));
82 name = class_getImageName(big);
83 testassert(name);
84 testassert(strstr(name, "unload2.bundle"));
85
86 id o1 = [small new];
87 id o2 = [big new];
88 testassert(o1);
89 testassert(o2);
90
91 // give BigClass and BigClass->isa large method caches (4692641)
92 // Flush caches part way through to test large empty caches.
93 for (i = 0; i < 3000; i++) {
94 sprintf(buf, "method_%d", i);
95 SEL sel = sel_registerName(buf);
96 ((void(*)(id, SEL))objc_msgSend)(o2, sel);
97 ((void(*)(id, SEL))objc_msgSend)(object_getClass(o2), sel);
98 }
99 _objc_flush_caches(object_getClass(o2));
100 for (i = 0; i < 17000; i++) {
101 sprintf(buf, "method_%d", i);
102 SEL sel = sel_registerName(buf);
103 ((void(*)(id, SEL))objc_msgSend)(o2, sel);
104 ((void(*)(id, SEL))objc_msgSend)(object_getClass(o2), sel);
105 }
106
107 RELEASE_VAR(o1);
108 RELEASE_VAR(o2);
109
110 testcollect();
111
112 int err = dlclose(bundle);
113 testassert(err == 0);
114 err = dlclose(bundle);
115 testassert(err == -1); // already closed
116
117 _objc_flush_caches(nil);
118
119 testassert(objc_getClass("SmallClass") == NULL);
120 testassert(objc_getClass("BigClass") == NULL);
121
122 names = objc_copyImageNames(&imageCount);
123 testassert(names);
124 testassert(imageCount == imageCount0);
125 testassert(! hasName(names, "unload2.bundle"));
126 free(names);
127
128 // these selectors came from the bundle
129 testassert(0 == strcmp("unload2_instance_method", sel_getName(sel_registerName("unload2_instance_method"))));
130 testassert(0 == strcmp("unload2_category_method", sel_getName(sel_registerName("unload2_category_method"))));
131
132 // This protocol came from the bundle.
133 // It isn't unloaded cleanly (rdar://20664713), but neither
134 // may it cause the protocol table to crash after unloading.
135 testassert(objc_getProtocol("SmallProtocol"));
136 }
137
138
139 int main()
140 {
141 char *useClosures = getenv("DYLD_USE_CLOSURES");
142 int dyld3 = useClosures != NULL && useClosures[0] != '0';
143
144 objc_setForwardHandler((void*)&forward_handler, (void*)&forward_handler);
145
146 #if defined(__arm__) || defined(__arm64__)
147 int count = 10;
148 #else
149 int count = is_guardmalloc() ? 10 : 100;
150 #endif
151
152 cycle();
153 #if __LP64__
154 // fixme heap use goes up 512 bytes after the 2nd cycle only - bad or not?
155 cycle();
156 #endif
157
158 leak_mark();
159 for (int i = 0; i < count; i++) {
160 cycle();
161 }
162 // dyld3 currently leaks 8 bytes for each dlopen/dlclose pair, so accommodate it. rdar://problem/53769254
163 leak_check(dyld3 ? (count * sizeof(void *)) : 0);
164
165 // 5359412 Make sure dylibs with nothing other than image_info can close
166 void *dylib = dlopen("unload3.dylib", RTLD_LAZY);
167 testassert(dylib);
168 int err = dlclose(dylib);
169 testassert(err == 0);
170 err = dlclose(dylib);
171 // dyld3 doesn't error when dlclosing the dylib twice. This is probably expected. rdar://problem/53769374
172 if (!dyld3)
173 testassert(err == -1); // already closed
174
175 // Make sure dylibs with real objc content cannot close
176 dylib = dlopen("unload4.dylib", RTLD_LAZY);
177 testassert(dylib);
178 err = dlclose(dylib);
179 testassert(err == 0);
180 err = dlclose(dylib);
181 // dyld3 doesn't error when dlclosing the dylib twice. This is probably expected. rdar://problem/53769374
182 if (!dyld3)
183 testassert(err == -1); // already closed
184
185 succeed(__FILE__);
186 }
187
188 #endif