1 #include <dlfcn.h>
2 #include <pthread.h>
3 #include <stdlib.h>
4 #include <sys/wait.h>
5 #include "test.h"
6 #include "global.h"
7
8 #define SO_FOR_NO_DELETE "lib_for_no_delete.so"
9 #define SO_FOR_DLOPEN "lib_for_dlopen.so"
10 #define SO_LOAD_BY_LOCAL "libdlopen_for_load_by_local_dso.so"
11 #define SO_LOAD_BY_GLOBAL "libdlopen_for_load_by_global_dso.so"
12 #define SO_CLOSE_RECURSIVE_OPEN_SO "libdlclose_recursive_dlopen_so.so"
13 #define NR_DLCLOSE_THREADS 10
14
15 typedef void(*TEST_PTR)(void);
16
do_dlopen(const char *name, int mode)17 void do_dlopen(const char *name, int mode)
18 {
19 void* handle = dlopen(name, mode);
20
21 if(!handle)
22 t_error("dlopen(name=%s, mode=%d) failed: %s\n", name, mode, dlerror());
23
24 if(dlclose(handle))
25 t_error("dlclose %s failed : %s \n", name, dlerror());
26 }
27
dlopen_lazynull28 void dlopen_lazy()
29 {
30 do_dlopen(SO_FOR_DLOPEN, RTLD_LAZY);
31 }
32
dlopen_nownull33 void dlopen_now()
34 {
35 do_dlopen(SO_FOR_DLOPEN, RTLD_NOW);
36 }
37
dlopen_globalnull38 void dlopen_global()
39 {
40 do_dlopen(SO_FOR_DLOPEN, RTLD_GLOBAL);
41 }
42
dlopen_localnull43 void dlopen_local()
44 {
45 do_dlopen(SO_FOR_DLOPEN, RTLD_LOCAL);
46 }
47
dlopen_so_used_by_dlsymnull48 void dlopen_so_used_by_dlsym()
49 {
50 void* handle1 = dlopen(SO_LOAD_BY_LOCAL, RTLD_LOCAL);
51 if(!handle1)
52 t_error("dlopen(name=%s, mode=%d) failed: %s\n", SO_LOAD_BY_LOCAL, RTLD_LOCAL, dlerror());
53
54 // dlsym can't see the so which is loaded by RTLD_LOCAL.
55 TEST_PTR for_local_ptr = dlsym(RTLD_DEFAULT, "for_local");
56 if (for_local_ptr != NULL) {
57 t_error("dlsym RTLD_LOCAL so(%s) should failed but get succeed.\n", "for_local");
58 }
59
60 if(dlclose(handle1))
61 t_error("dlclose %s failed : %s \n", SO_LOAD_BY_LOCAL, dlerror());
62
63
64 void* handle2 = dlopen(SO_LOAD_BY_GLOBAL, RTLD_GLOBAL);
65 if(!handle2)
66 t_error("dlopen(name=%s, mode=%d) failed: %s\n", SO_LOAD_BY_GLOBAL, RTLD_LOCAL, dlerror());
67
68 // dlsym can see the so which is loaded by RTLD_DEFAULT even without dependencies.
69 TEST_PTR for_global_ptr = dlsym(RTLD_DEFAULT, "for_global");
70 if (!for_global_ptr) {
71 t_error("dlsym RTLD_GLOBAL so(%s) should succeed but get failed: %s \n", "for_global", dlerror());
72 }
73
74 if(dlclose(handle2))
75 t_error("dlclose %s failed : %s \n", SO_LOAD_BY_GLOBAL, dlerror());
76 }
77
dlopen_nodelete_and_noloadnull78 void dlopen_nodelete_and_noload()
79 {
80 void* handle1 = dlopen(SO_FOR_NO_DELETE, RTLD_NODELETE);
81
82 if(!handle1)
83 t_error("dlopen(name=%s, mode=RTLD_NODELETE) failed: %s\n", SO_FOR_NO_DELETE, dlerror());
84
85 if(dlclose(handle1))
86 t_error("dlclose %s failed : %s \n", SO_FOR_NO_DELETE, dlerror());
87
88
89 void* handle2 = dlopen(SO_FOR_NO_DELETE, RTLD_NOLOAD);
90 if(!handle2)
91 t_error("dlopen(name=%s, mode=RTLD_NOLOAD) failed: %s\n", SO_FOR_NO_DELETE, dlerror());
92
93 if (handle1 != handle2) {
94 t_error("dlopen %s by RTLD_NODELETE but get different handle when dlopen by RTLD_NOLOAD again.\n", SO_FOR_NO_DELETE);
95 }
96 }
97
dlopen_dlclosenull98 void dlopen_dlclose()
99 {
100 void* handle = dlopen(SO_FOR_DLOPEN, RTLD_LOCAL);
101 if(!handle)
102 t_error("dlopen(name=%s, mode=%d) failed: %s\n", SO_FOR_DLOPEN, RTLD_LOCAL, dlerror());
103
104 handle = dlopen(SO_FOR_DLOPEN, RTLD_LOCAL);
105 if(!handle)
106 t_error("dlopen(name=%s, mode=%d) failed: %s\n", SO_FOR_DLOPEN, RTLD_LOCAL, dlerror());
107
108 if(dlclose(handle))
109 t_error("dlclose %s failed : %s \n", SO_FOR_DLOPEN, dlerror());
110
111 // lib should still exist in memory.
112 handle = dlopen(SO_FOR_DLOPEN, RTLD_NOLOAD);
113 if(!handle)
114 t_error("dlopen(name=%s, mode=%d) failed: %s\n", SO_FOR_DLOPEN, RTLD_LOCAL, dlerror());
115
116 if(dlclose(handle))
117 t_error("dlclose %s failed : %s \n", SO_FOR_DLOPEN, dlerror());
118
119 // It need to do one more dlclose because call dlopen by RTLD_NOLOAD add reference counting.
120 if(dlclose(handle))
121 t_error("dlclose %s failed : %s \n", SO_FOR_DLOPEN, dlerror());
122
123 // dlopen and dlclose call counts match so the lib should not exist in memory.
124 handle = dlopen(SO_FOR_DLOPEN, RTLD_NOLOAD);
125 if(handle) {
126 t_error("dlopen(name=%s, mode=%d) failed: %s\n", SO_FOR_DLOPEN, RTLD_LOCAL, dlerror());
127 dlclose(handle);
128 }
129 }
130
131 #define DLOPEN_WEAK "libdlopen_weak.so"
132 typedef int (*func_ptr)();
133
dlopen_dlclose_weaknull134 void dlopen_dlclose_weak()
135 {
136 void* handle = dlopen(DLOPEN_WEAK, RTLD_LAZY | RTLD_GLOBAL);
137 if (!handle)
138 t_error("dlopen(name=%s, mode=%d) failed: %s\n", DLOPEN_WEAK, RTLD_LAZY | RTLD_GLOBAL, dlerror());
139 func_ptr fn = (func_ptr)dlsym(handle, "test_number");
140 if (fn) {
141 int ret = fn();
142 if (ret != GLOBAL_VALUE)
143 t_error("weak symbol relocation error: so_name: %s, symbol: test_number\n", DLOPEN_WEAK);
144 }
145 dlclose(handle);
146 }
147
dlclose_recursivenull148 void dlclose_recursive()
149 {
150 void *handle = dlopen(SO_CLOSE_RECURSIVE_OPEN_SO, RTLD_LAZY | RTLD_LOCAL);
151 if (!handle)
152 t_error("dlopen(name=%s, mode=%d) failed: %s\n", SO_CLOSE_RECURSIVE_OPEN_SO, RTLD_LAZY | RTLD_LOCAL, dlerror());
153
154 /* close handle normally, if libc doesn't support close .so file recursivly
155 * it will be deedlock, and timed out error will happen
156 */
157 dlclose(handle);
158 }
159
dlclose_recursive_threadnull160 void *dlclose_recursive_thread()
161 {
162 dlclose_recursive();
163 return NULL;
164 }
165
dlclose_recursive_by_multipthreadnull166 void dlclose_recursive_by_multipthread()
167 {
168 pthread_t testThreads[NR_DLCLOSE_THREADS] = {0};
169 for (int i = 0; i < NR_DLCLOSE_THREADS; ++i) {
170 pthread_create(&testThreads[i], NULL, dlclose_recursive_thread, NULL);
171 }
172
173 for (int i = 0; i < NR_DLCLOSE_THREADS; ++i) {
174 pthread_join(testThreads[i], NULL);
175 }
176 }
177
178 #define DLOPEN_GLOBAL "libdlopen_global.so"
179 #define DLOPEN_LOCAL "libdlopen_local.so"
180
dlopen_global_testnull181 void dlopen_global_test()
182 {
183 int value;
184 void *global_handler = dlopen(DLOPEN_GLOBAL, RTLD_GLOBAL);
185 if (!global_handler)
186 t_error("dlopen(name=%s, mode=%d) failed: %s\n", DLOPEN_GLOBAL, RTLD_GLOBAL, dlerror());
187 func_ptr global_fn = (func_ptr)dlsym(global_handler, "global_caller");
188 if (global_fn) {
189 value = global_fn();
190 if (value != GLOBAL_VALUE)
191 t_error("global caller returned: %d, expected: %d\n", value, GLOBAL_VALUE);
192 }
193
194 void *local_handler = dlopen(DLOPEN_LOCAL, RTLD_LOCAL);
195 if (!local_handler)
196 t_error("dlopen(name=%s, mode=%d) failed: %s\n", DLOPEN_LOCAL, RTLD_LOCAL, dlerror());
197 func_ptr local_fn = (func_ptr)dlsym(local_handler, "local_caller");
198 if (local_fn) {
199 value = local_fn();
200 if (value != GLOBAL_VALUE)
201 t_error("local caller returned: %d, expected: %d\n", value, GLOBAL_VALUE);
202 }
203 dlclose(global_handler);
204 dlclose(local_handler);
205 }
206
207 #define DLCLOSE_EXIT_DEAD_LOCK "libdl_multithread_test_dso.so"
208
dlclose_exit_testnull209 void dlclose_exit_test()
210 {
211 int status;
212 void* handle;
213 int pid = fork();
214 switch (pid) {
215 case -1:
216 t_error("fork failed: %d\n", __LINE__);
217 break;
218 case 0:
219 handle = dlopen(DLCLOSE_EXIT_DEAD_LOCK, RTLD_GLOBAL);
220 if (!handle) {
221 t_error("dlclose_exit_test dlopen %s failed: %s", DLCLOSE_EXIT_DEAD_LOCK, dlerror());
222 exit(EXIT_FAILURE);
223 }
224 exit(EXIT_SUCCESS);
225 default:
226 waitpid(pid, &status, WUNTRACED);
227 if (WIFEXITED(status)) {
228 if (WEXITSTATUS(status) != EXIT_SUCCESS) {
229 t_error("dlclose_exit_test failed");
230 };
231 }
232 break;
233 }
234 return;
235 }
236
237 #define DLCLOSE_WITH_TLS "libdlclose_tls.so"
238 typedef void *(*functype)(void);
dlclose_with_tls_testnull239 void dlclose_with_tls_test()
240 {
241 void* handle = dlopen(DLCLOSE_WITH_TLS, RTLD_GLOBAL);
242 if (!handle) {
243 t_error("dlopen(name=%s, mode=%d) failed: %s\n", DLCLOSE_WITH_TLS, RTLD_GLOBAL, dlerror());
244 }
245 functype func = (functype)dlsym(handle, "foo_ctor");
246 func();
247 dlclose(handle);
248 }
249
main(int argc, char *argv[])250 int main(int argc, char *argv[])
251 {
252 void *h, *g;
253 int *i, *i2;
254 char *s;
255 void (*f)(void);
256 char buf[512];
257
258 if (!t_pathrel(buf, sizeof buf, argv[0], "libdlopen_dso.so")) {
259 t_error("failed to obtain relative path to dlopen_dso.so\n");
260 return 1;
261 }
262 h = dlopen(buf, RTLD_LAZY|RTLD_LOCAL);
263 if (!h)
264 t_error("dlopen %s failed: %s\n", buf, dlerror());
265 i = dlsym(h, "i");
266 if (!i)
267 t_error("dlsym i failed: %s\n", dlerror());
268 if (*i != 1)
269 t_error("initialization failed: want i=1 got i=%d\n", *i);
270 f = (void (*)(void))dlsym(h, "f");
271 if (!f)
272 t_error("dlsym f failed: %s\n", dlerror());
273 f();
274 if (*i != 2)
275 t_error("f call failed: want i=2 got i=%d\n", *i);
276
277 g = dlopen(0, RTLD_LAZY|RTLD_LOCAL);
278 if (!g)
279 t_error("dlopen 0 failed: %s\n", dlerror());
280 i2 = dlsym(g, "i");
281 s = dlerror();
282 if (i2 || s == 0)
283 t_error("dlsym i should have failed\n");
284 if (dlsym(g, "main") != (void*)main)
285 t_error("dlsym main failed: %s\n", dlerror());
286
287 /* close+open reinitializes the dso with glibc but not with musl */
288 h = dlopen(buf, RTLD_LAZY|RTLD_GLOBAL);
289 i2 = dlsym(g, "i");
290 if (!i2)
291 t_error("dlsym i failed: %s\n", dlerror());
292 if (i2 != i)
293 t_error("reopened dso should have the same symbols, want %p, got %p\n", i, i2);
294 if (*i2 != 2)
295 t_error("reopened dso should have the same symbols, want i2==2, got i2==%d\n", *i2);
296 if (dlclose(g))
297 t_error("dlclose failed: %s\n", dlerror());
298 if (dlclose(h))
299 t_error("dlclose failed: %s\n", dlerror());
300
301 dlopen_lazy();
302 dlopen_now();
303 dlopen_global();
304 dlopen_local();
305 dlopen_so_used_by_dlsym();
306 dlopen_nodelete_and_noload();
307 dlopen_dlclose();
308 dlopen_dlclose_weak();
309 dlclose_recursive();
310 dlclose_recursive_by_multipthread();
311 dlopen_global_test();
312 dlclose_exit_test();
313 dlclose_with_tls_test();
314
315 return t_status;
316 }
317