]>
Commit | Line | Data |
---|---|---|
13ba007e A |
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 | |
1807f628 | 7 | $C{COMPILE} $DIR/unload2.m -o unload2.bundle -bundle $C{FORCE_LOAD_ARCLITE} -Xlinker -undefined -Xlinker dynamic_lookup |
13ba007e A |
8 | $C{COMPILE} $DIR/unload.m -o unload.exe -framework Foundation |
9 | END | |
10 | */ | |
11 | ||
12 | /* | |
13ba007e | 13 | TEST_BUILD_OUTPUT |
1807f628 | 14 | ld: warning: -undefined dynamic_lookup is deprecated on .* |
13ba007e A |
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"); | |
1807f628 | 74 | Class missing = objc_getClass("SubclassOfMissingWeakImport"); |
13ba007e A |
75 | testassert(small); |
76 | testassert(big); | |
1807f628 | 77 | testassert(!missing); |
13ba007e A |
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 | { | |
34d5b5e8 A |
141 | char *useClosures = getenv("DYLD_USE_CLOSURES"); |
142 | int dyld3 = useClosures != NULL && useClosures[0] != '0'; | |
143 | ||
13ba007e A |
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(); | |
34d5b5e8 | 159 | for (int i = 0; i < count; i++) { |
13ba007e A |
160 | cycle(); |
161 | } | |
34d5b5e8 A |
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); | |
13ba007e A |
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); | |
34d5b5e8 A |
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 | |
13ba007e A |
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); | |
34d5b5e8 A |
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 | |
13ba007e A |
184 | |
185 | succeed(__FILE__); | |
186 | } | |
187 | ||
188 | #endif |