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