1570af302Sopenharmony_ci#include <dlfcn.h>
2570af302Sopenharmony_ci#include <pthread.h>
3570af302Sopenharmony_ci#include <stdlib.h>
4570af302Sopenharmony_ci#include <sys/wait.h>
5570af302Sopenharmony_ci#include "test.h"
6570af302Sopenharmony_ci#include "global.h"
7570af302Sopenharmony_ci
8570af302Sopenharmony_ci#define SO_FOR_NO_DELETE "lib_for_no_delete.so"
9570af302Sopenharmony_ci#define SO_FOR_DLOPEN "lib_for_dlopen.so"
10570af302Sopenharmony_ci#define SO_LOAD_BY_LOCAL "libdlopen_for_load_by_local_dso.so"
11570af302Sopenharmony_ci#define SO_LOAD_BY_GLOBAL "libdlopen_for_load_by_global_dso.so"
12570af302Sopenharmony_ci#define SO_CLOSE_RECURSIVE_OPEN_SO "libdlclose_recursive_dlopen_so.so"
13570af302Sopenharmony_ci#define NR_DLCLOSE_THREADS 10
14570af302Sopenharmony_ci
15570af302Sopenharmony_citypedef void(*TEST_PTR)(void);
16570af302Sopenharmony_ci
17570af302Sopenharmony_civoid do_dlopen(const char *name, int mode)
18570af302Sopenharmony_ci{
19570af302Sopenharmony_ci	void* handle = dlopen(name, mode);
20570af302Sopenharmony_ci
21570af302Sopenharmony_ci	if(!handle)
22570af302Sopenharmony_ci		t_error("dlopen(name=%s, mode=%d) failed: %s\n", name, mode, dlerror());
23570af302Sopenharmony_ci
24570af302Sopenharmony_ci	if(dlclose(handle))
25570af302Sopenharmony_ci		t_error("dlclose %s failed : %s \n", name, dlerror());
26570af302Sopenharmony_ci}
27570af302Sopenharmony_ci
28570af302Sopenharmony_civoid dlopen_lazy()
29570af302Sopenharmony_ci{
30570af302Sopenharmony_ci	do_dlopen(SO_FOR_DLOPEN, RTLD_LAZY);
31570af302Sopenharmony_ci}
32570af302Sopenharmony_ci
33570af302Sopenharmony_civoid dlopen_now()
34570af302Sopenharmony_ci{
35570af302Sopenharmony_ci	do_dlopen(SO_FOR_DLOPEN, RTLD_NOW);
36570af302Sopenharmony_ci}
37570af302Sopenharmony_ci
38570af302Sopenharmony_civoid dlopen_global()
39570af302Sopenharmony_ci{
40570af302Sopenharmony_ci	do_dlopen(SO_FOR_DLOPEN, RTLD_GLOBAL);
41570af302Sopenharmony_ci}
42570af302Sopenharmony_ci
43570af302Sopenharmony_civoid dlopen_local()
44570af302Sopenharmony_ci{
45570af302Sopenharmony_ci	do_dlopen(SO_FOR_DLOPEN, RTLD_LOCAL);
46570af302Sopenharmony_ci}
47570af302Sopenharmony_ci
48570af302Sopenharmony_civoid dlopen_so_used_by_dlsym()
49570af302Sopenharmony_ci{
50570af302Sopenharmony_ci	void* handle1 = dlopen(SO_LOAD_BY_LOCAL, RTLD_LOCAL);
51570af302Sopenharmony_ci	if(!handle1)
52570af302Sopenharmony_ci		t_error("dlopen(name=%s, mode=%d) failed: %s\n", SO_LOAD_BY_LOCAL, RTLD_LOCAL, dlerror());
53570af302Sopenharmony_ci
54570af302Sopenharmony_ci	// dlsym can't see the so which is loaded by RTLD_LOCAL.
55570af302Sopenharmony_ci	TEST_PTR for_local_ptr = dlsym(RTLD_DEFAULT, "for_local");
56570af302Sopenharmony_ci	if (for_local_ptr != NULL) {
57570af302Sopenharmony_ci		t_error("dlsym RTLD_LOCAL so(%s) should failed but get succeed.\n", "for_local");
58570af302Sopenharmony_ci	}
59570af302Sopenharmony_ci
60570af302Sopenharmony_ci	if(dlclose(handle1))
61570af302Sopenharmony_ci		t_error("dlclose %s failed : %s \n", SO_LOAD_BY_LOCAL, dlerror());
62570af302Sopenharmony_ci
63570af302Sopenharmony_ci
64570af302Sopenharmony_ci	void* handle2 = dlopen(SO_LOAD_BY_GLOBAL, RTLD_GLOBAL);
65570af302Sopenharmony_ci	if(!handle2)
66570af302Sopenharmony_ci		t_error("dlopen(name=%s, mode=%d) failed: %s\n", SO_LOAD_BY_GLOBAL, RTLD_LOCAL, dlerror());
67570af302Sopenharmony_ci
68570af302Sopenharmony_ci	// dlsym can see the so which is loaded by RTLD_DEFAULT even without dependencies.
69570af302Sopenharmony_ci	TEST_PTR for_global_ptr = dlsym(RTLD_DEFAULT, "for_global");
70570af302Sopenharmony_ci	if (!for_global_ptr) {
71570af302Sopenharmony_ci		t_error("dlsym RTLD_GLOBAL so(%s) should succeed but get failed: %s \n", "for_global", dlerror());
72570af302Sopenharmony_ci	}
73570af302Sopenharmony_ci
74570af302Sopenharmony_ci	if(dlclose(handle2))
75570af302Sopenharmony_ci		t_error("dlclose %s failed : %s \n", SO_LOAD_BY_GLOBAL, dlerror());
76570af302Sopenharmony_ci}
77570af302Sopenharmony_ci
78570af302Sopenharmony_civoid dlopen_nodelete_and_noload()
79570af302Sopenharmony_ci{
80570af302Sopenharmony_ci	void* handle1 = dlopen(SO_FOR_NO_DELETE, RTLD_NODELETE);
81570af302Sopenharmony_ci
82570af302Sopenharmony_ci	if(!handle1)
83570af302Sopenharmony_ci		t_error("dlopen(name=%s, mode=RTLD_NODELETE) failed: %s\n", SO_FOR_NO_DELETE, dlerror());
84570af302Sopenharmony_ci
85570af302Sopenharmony_ci	if(dlclose(handle1))
86570af302Sopenharmony_ci		t_error("dlclose %s failed : %s \n", SO_FOR_NO_DELETE, dlerror());
87570af302Sopenharmony_ci
88570af302Sopenharmony_ci
89570af302Sopenharmony_ci	void* handle2 = dlopen(SO_FOR_NO_DELETE, RTLD_NOLOAD);
90570af302Sopenharmony_ci	if(!handle2)
91570af302Sopenharmony_ci		t_error("dlopen(name=%s, mode=RTLD_NOLOAD) failed: %s\n", SO_FOR_NO_DELETE, dlerror());
92570af302Sopenharmony_ci
93570af302Sopenharmony_ci	if (handle1 != handle2) {
94570af302Sopenharmony_ci		t_error("dlopen %s by RTLD_NODELETE but get different handle when dlopen by RTLD_NOLOAD again.\n", SO_FOR_NO_DELETE);
95570af302Sopenharmony_ci	}
96570af302Sopenharmony_ci}
97570af302Sopenharmony_ci
98570af302Sopenharmony_civoid dlopen_dlclose()
99570af302Sopenharmony_ci{
100570af302Sopenharmony_ci	void* handle = dlopen(SO_FOR_DLOPEN, RTLD_LOCAL);
101570af302Sopenharmony_ci	if(!handle)
102570af302Sopenharmony_ci		t_error("dlopen(name=%s, mode=%d) failed: %s\n", SO_FOR_DLOPEN, RTLD_LOCAL, dlerror());
103570af302Sopenharmony_ci
104570af302Sopenharmony_ci	handle = dlopen(SO_FOR_DLOPEN, RTLD_LOCAL);
105570af302Sopenharmony_ci	if(!handle)
106570af302Sopenharmony_ci		t_error("dlopen(name=%s, mode=%d) failed: %s\n", SO_FOR_DLOPEN, RTLD_LOCAL, dlerror());
107570af302Sopenharmony_ci
108570af302Sopenharmony_ci	if(dlclose(handle))
109570af302Sopenharmony_ci		t_error("dlclose %s failed : %s \n", SO_FOR_DLOPEN, dlerror());
110570af302Sopenharmony_ci
111570af302Sopenharmony_ci	// lib should still exist in memory.
112570af302Sopenharmony_ci	handle = dlopen(SO_FOR_DLOPEN, RTLD_NOLOAD);
113570af302Sopenharmony_ci	if(!handle)
114570af302Sopenharmony_ci		t_error("dlopen(name=%s, mode=%d) failed: %s\n", SO_FOR_DLOPEN, RTLD_LOCAL, dlerror());
115570af302Sopenharmony_ci
116570af302Sopenharmony_ci	if(dlclose(handle))
117570af302Sopenharmony_ci			t_error("dlclose %s failed : %s \n", SO_FOR_DLOPEN, dlerror());
118570af302Sopenharmony_ci
119570af302Sopenharmony_ci	// It need to do one more dlclose because call dlopen by RTLD_NOLOAD add reference counting.
120570af302Sopenharmony_ci	if(dlclose(handle))
121570af302Sopenharmony_ci			t_error("dlclose %s failed : %s \n", SO_FOR_DLOPEN, dlerror());
122570af302Sopenharmony_ci
123570af302Sopenharmony_ci	// dlopen and dlclose call counts match so the lib should not exist in memory.
124570af302Sopenharmony_ci	handle = dlopen(SO_FOR_DLOPEN, RTLD_NOLOAD);
125570af302Sopenharmony_ci	if(handle) {
126570af302Sopenharmony_ci		t_error("dlopen(name=%s, mode=%d) failed: %s\n", SO_FOR_DLOPEN, RTLD_LOCAL, dlerror());
127570af302Sopenharmony_ci		dlclose(handle);
128570af302Sopenharmony_ci	}
129570af302Sopenharmony_ci}
130570af302Sopenharmony_ci
131570af302Sopenharmony_ci#define DLOPEN_WEAK "libdlopen_weak.so"
132570af302Sopenharmony_citypedef int (*func_ptr)();
133570af302Sopenharmony_ci
134570af302Sopenharmony_civoid dlopen_dlclose_weak()
135570af302Sopenharmony_ci{
136570af302Sopenharmony_ci	void* handle = dlopen(DLOPEN_WEAK, RTLD_LAZY | RTLD_GLOBAL);
137570af302Sopenharmony_ci	if (!handle)
138570af302Sopenharmony_ci		t_error("dlopen(name=%s, mode=%d) failed: %s\n", DLOPEN_WEAK, RTLD_LAZY | RTLD_GLOBAL, dlerror());
139570af302Sopenharmony_ci	func_ptr fn = (func_ptr)dlsym(handle, "test_number");
140570af302Sopenharmony_ci	if (fn) {
141570af302Sopenharmony_ci		int ret = fn();
142570af302Sopenharmony_ci		if (ret != GLOBAL_VALUE)
143570af302Sopenharmony_ci			t_error("weak symbol relocation error: so_name: %s, symbol: test_number\n", DLOPEN_WEAK);
144570af302Sopenharmony_ci	}
145570af302Sopenharmony_ci	dlclose(handle);
146570af302Sopenharmony_ci}
147570af302Sopenharmony_ci
148570af302Sopenharmony_civoid dlclose_recursive()
149570af302Sopenharmony_ci{
150570af302Sopenharmony_ci    void *handle = dlopen(SO_CLOSE_RECURSIVE_OPEN_SO, RTLD_LAZY | RTLD_LOCAL);
151570af302Sopenharmony_ci    if (!handle)
152570af302Sopenharmony_ci        t_error("dlopen(name=%s, mode=%d) failed: %s\n", SO_CLOSE_RECURSIVE_OPEN_SO, RTLD_LAZY | RTLD_LOCAL, dlerror());
153570af302Sopenharmony_ci
154570af302Sopenharmony_ci    /* close handle normally, if libc doesn't support close .so file recursivly
155570af302Sopenharmony_ci     * it will be deedlock, and timed out error will happen
156570af302Sopenharmony_ci     */
157570af302Sopenharmony_ci    dlclose(handle);
158570af302Sopenharmony_ci}
159570af302Sopenharmony_ci
160570af302Sopenharmony_civoid *dlclose_recursive_thread()
161570af302Sopenharmony_ci{
162570af302Sopenharmony_ci	dlclose_recursive();
163570af302Sopenharmony_ci	return NULL;
164570af302Sopenharmony_ci}
165570af302Sopenharmony_ci
166570af302Sopenharmony_civoid dlclose_recursive_by_multipthread()
167570af302Sopenharmony_ci{
168570af302Sopenharmony_ci	pthread_t testThreads[NR_DLCLOSE_THREADS] = {0};
169570af302Sopenharmony_ci	for (int i = 0; i < NR_DLCLOSE_THREADS; ++i) {
170570af302Sopenharmony_ci		pthread_create(&testThreads[i], NULL, dlclose_recursive_thread, NULL);
171570af302Sopenharmony_ci	}
172570af302Sopenharmony_ci
173570af302Sopenharmony_ci	for (int i = 0; i < NR_DLCLOSE_THREADS; ++i) {
174570af302Sopenharmony_ci		pthread_join(testThreads[i], NULL);
175570af302Sopenharmony_ci	}
176570af302Sopenharmony_ci}
177570af302Sopenharmony_ci
178570af302Sopenharmony_ci#define DLOPEN_GLOBAL "libdlopen_global.so"
179570af302Sopenharmony_ci#define DLOPEN_LOCAL "libdlopen_local.so"
180570af302Sopenharmony_ci
181570af302Sopenharmony_civoid dlopen_global_test()
182570af302Sopenharmony_ci{
183570af302Sopenharmony_ci	int value;
184570af302Sopenharmony_ci	void *global_handler = dlopen(DLOPEN_GLOBAL, RTLD_GLOBAL);
185570af302Sopenharmony_ci	if (!global_handler)
186570af302Sopenharmony_ci		t_error("dlopen(name=%s, mode=%d) failed: %s\n", DLOPEN_GLOBAL, RTLD_GLOBAL, dlerror());
187570af302Sopenharmony_ci	func_ptr global_fn = (func_ptr)dlsym(global_handler, "global_caller");
188570af302Sopenharmony_ci	if (global_fn) {
189570af302Sopenharmony_ci		value = global_fn();
190570af302Sopenharmony_ci		if (value != GLOBAL_VALUE)
191570af302Sopenharmony_ci			t_error("global caller returned: %d, expected: %d\n", value, GLOBAL_VALUE);
192570af302Sopenharmony_ci	}
193570af302Sopenharmony_ci
194570af302Sopenharmony_ci	void *local_handler = dlopen(DLOPEN_LOCAL, RTLD_LOCAL);
195570af302Sopenharmony_ci	if (!local_handler)
196570af302Sopenharmony_ci		t_error("dlopen(name=%s, mode=%d) failed: %s\n", DLOPEN_LOCAL, RTLD_LOCAL, dlerror());
197570af302Sopenharmony_ci	func_ptr local_fn = (func_ptr)dlsym(local_handler, "local_caller");
198570af302Sopenharmony_ci	if (local_fn) {
199570af302Sopenharmony_ci		value = local_fn();
200570af302Sopenharmony_ci		if (value != GLOBAL_VALUE)
201570af302Sopenharmony_ci			t_error("local caller returned: %d, expected: %d\n", value, GLOBAL_VALUE);
202570af302Sopenharmony_ci	}
203570af302Sopenharmony_ci	dlclose(global_handler);
204570af302Sopenharmony_ci	dlclose(local_handler);
205570af302Sopenharmony_ci}
206570af302Sopenharmony_ci
207570af302Sopenharmony_ci#define DLCLOSE_EXIT_DEAD_LOCK "libdl_multithread_test_dso.so"
208570af302Sopenharmony_ci
209570af302Sopenharmony_civoid dlclose_exit_test()
210570af302Sopenharmony_ci{
211570af302Sopenharmony_ci	int status;
212570af302Sopenharmony_ci	void* handle;
213570af302Sopenharmony_ci	int pid = fork();
214570af302Sopenharmony_ci	switch (pid) {
215570af302Sopenharmony_ci        case -1:
216570af302Sopenharmony_ci            t_error("fork failed: %d\n", __LINE__);
217570af302Sopenharmony_ci            break;
218570af302Sopenharmony_ci        case 0:
219570af302Sopenharmony_ci            handle = dlopen(DLCLOSE_EXIT_DEAD_LOCK, RTLD_GLOBAL);
220570af302Sopenharmony_ci			if (!handle) {
221570af302Sopenharmony_ci				t_error("dlclose_exit_test dlopen %s failed: %s", DLCLOSE_EXIT_DEAD_LOCK, dlerror());
222570af302Sopenharmony_ci				exit(EXIT_FAILURE);
223570af302Sopenharmony_ci			}
224570af302Sopenharmony_ci            exit(EXIT_SUCCESS);
225570af302Sopenharmony_ci        default:
226570af302Sopenharmony_ci            waitpid(pid, &status, WUNTRACED);
227570af302Sopenharmony_ci			if (WIFEXITED(status)) {
228570af302Sopenharmony_ci				if (WEXITSTATUS(status) != EXIT_SUCCESS) {
229570af302Sopenharmony_ci					t_error("dlclose_exit_test failed");
230570af302Sopenharmony_ci				};
231570af302Sopenharmony_ci			}
232570af302Sopenharmony_ci            break;
233570af302Sopenharmony_ci    }
234570af302Sopenharmony_ci    return;
235570af302Sopenharmony_ci}
236570af302Sopenharmony_ci
237570af302Sopenharmony_ci#define DLCLOSE_WITH_TLS "libdlclose_tls.so"
238570af302Sopenharmony_citypedef void *(*functype)(void);
239570af302Sopenharmony_civoid dlclose_with_tls_test()
240570af302Sopenharmony_ci{
241570af302Sopenharmony_ci	void* handle = dlopen(DLCLOSE_WITH_TLS, RTLD_GLOBAL);
242570af302Sopenharmony_ci	if (!handle) {
243570af302Sopenharmony_ci		t_error("dlopen(name=%s, mode=%d) failed: %s\n", DLCLOSE_WITH_TLS, RTLD_GLOBAL, dlerror());
244570af302Sopenharmony_ci	}
245570af302Sopenharmony_ci	functype func = (functype)dlsym(handle, "foo_ctor");
246570af302Sopenharmony_ci	func();
247570af302Sopenharmony_ci	dlclose(handle);
248570af302Sopenharmony_ci}
249570af302Sopenharmony_ci
250570af302Sopenharmony_ciint main(int argc, char *argv[])
251570af302Sopenharmony_ci{
252570af302Sopenharmony_ci	void *h, *g;
253570af302Sopenharmony_ci	int *i, *i2;
254570af302Sopenharmony_ci	char *s;
255570af302Sopenharmony_ci	void (*f)(void);
256570af302Sopenharmony_ci	char buf[512];
257570af302Sopenharmony_ci
258570af302Sopenharmony_ci	if (!t_pathrel(buf, sizeof buf, argv[0], "libdlopen_dso.so")) {
259570af302Sopenharmony_ci		t_error("failed to obtain relative path to dlopen_dso.so\n");
260570af302Sopenharmony_ci		return 1;
261570af302Sopenharmony_ci	}
262570af302Sopenharmony_ci	h = dlopen(buf, RTLD_LAZY|RTLD_LOCAL);
263570af302Sopenharmony_ci	if (!h)
264570af302Sopenharmony_ci		t_error("dlopen %s failed: %s\n", buf, dlerror());
265570af302Sopenharmony_ci	i = dlsym(h, "i");
266570af302Sopenharmony_ci	if (!i)
267570af302Sopenharmony_ci		t_error("dlsym i failed: %s\n", dlerror());
268570af302Sopenharmony_ci	if (*i != 1)
269570af302Sopenharmony_ci		t_error("initialization failed: want i=1 got i=%d\n", *i);
270570af302Sopenharmony_ci	f = (void (*)(void))dlsym(h, "f");
271570af302Sopenharmony_ci	if (!f)
272570af302Sopenharmony_ci		t_error("dlsym f failed: %s\n", dlerror());
273570af302Sopenharmony_ci	f();
274570af302Sopenharmony_ci	if (*i != 2)
275570af302Sopenharmony_ci		t_error("f call failed: want i=2 got i=%d\n", *i);
276570af302Sopenharmony_ci
277570af302Sopenharmony_ci	g = dlopen(0, RTLD_LAZY|RTLD_LOCAL);
278570af302Sopenharmony_ci	if (!g)
279570af302Sopenharmony_ci		t_error("dlopen 0 failed: %s\n", dlerror());
280570af302Sopenharmony_ci	i2 = dlsym(g, "i");
281570af302Sopenharmony_ci	s = dlerror();
282570af302Sopenharmony_ci	if (i2 || s == 0)
283570af302Sopenharmony_ci		t_error("dlsym i should have failed\n");
284570af302Sopenharmony_ci	if (dlsym(g, "main") != (void*)main)
285570af302Sopenharmony_ci		t_error("dlsym main failed: %s\n", dlerror());
286570af302Sopenharmony_ci
287570af302Sopenharmony_ci	/* close+open reinitializes the dso with glibc but not with musl */
288570af302Sopenharmony_ci	h = dlopen(buf, RTLD_LAZY|RTLD_GLOBAL);
289570af302Sopenharmony_ci	i2 = dlsym(g, "i");
290570af302Sopenharmony_ci	if (!i2)
291570af302Sopenharmony_ci		t_error("dlsym i failed: %s\n", dlerror());
292570af302Sopenharmony_ci	if (i2 != i)
293570af302Sopenharmony_ci		t_error("reopened dso should have the same symbols, want %p, got %p\n", i, i2);
294570af302Sopenharmony_ci	if (*i2 != 2)
295570af302Sopenharmony_ci		t_error("reopened dso should have the same symbols, want i2==2, got i2==%d\n", *i2);
296570af302Sopenharmony_ci	if (dlclose(g))
297570af302Sopenharmony_ci		t_error("dlclose failed: %s\n", dlerror());
298570af302Sopenharmony_ci	if (dlclose(h))
299570af302Sopenharmony_ci		t_error("dlclose failed: %s\n", dlerror());
300570af302Sopenharmony_ci
301570af302Sopenharmony_ci	dlopen_lazy();
302570af302Sopenharmony_ci	dlopen_now();
303570af302Sopenharmony_ci	dlopen_global();
304570af302Sopenharmony_ci	dlopen_local();
305570af302Sopenharmony_ci	dlopen_so_used_by_dlsym();
306570af302Sopenharmony_ci	dlopen_nodelete_and_noload();
307570af302Sopenharmony_ci	dlopen_dlclose();
308570af302Sopenharmony_ci	dlopen_dlclose_weak();
309570af302Sopenharmony_ci	dlclose_recursive();
310570af302Sopenharmony_ci	dlclose_recursive_by_multipthread();
311570af302Sopenharmony_ci	dlopen_global_test();
312570af302Sopenharmony_ci	dlclose_exit_test();
313570af302Sopenharmony_ci	dlclose_with_tls_test();
314570af302Sopenharmony_ci
315570af302Sopenharmony_ci	return t_status;
316570af302Sopenharmony_ci}
317