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
15typedef void(*TEST_PTR)(void);
16
17void 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
28void dlopen_lazy()
29{
30	do_dlopen(SO_FOR_DLOPEN, RTLD_LAZY);
31}
32
33void dlopen_now()
34{
35	do_dlopen(SO_FOR_DLOPEN, RTLD_NOW);
36}
37
38void dlopen_global()
39{
40	do_dlopen(SO_FOR_DLOPEN, RTLD_GLOBAL);
41}
42
43void dlopen_local()
44{
45	do_dlopen(SO_FOR_DLOPEN, RTLD_LOCAL);
46}
47
48void 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
78void 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
98void 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"
132typedef int (*func_ptr)();
133
134void 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
148void 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
160void *dlclose_recursive_thread()
161{
162	dlclose_recursive();
163	return NULL;
164}
165
166void 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
181void 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
209void 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"
238typedef void *(*functype)(void);
239void 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
250int 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