1 /*
2 * Copyright (C) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #include <dlfcn.h>
16 #include <pthread.h>
17 #include <stdio.h>
18 #include <string.h>
19 #include <stdlib.h>
20 #include <errno.h>
21 #include <link.h>
22 #include <sys/wait.h>
23
24 #include "test.h"
25
26 #define MAX_BUF 256
27 #define TEST_NUM 1000
28 #define TEST_SIZE 4096
29
30 const char* g_libPath = "/data/local/tmp/libc-test-lib/libdlopen_dso.so";
31 const char* g_initlibPath = "/data/local/tmp/libc-test-lib/libdlopen_init.so";
32 void* g_init_handler = NULL;
33 static int test_value = 0;
34
check_loaded(char* so)35 int check_loaded(char* so)
36 {
37 int pid = getpid();
38 char path[MAX_BUF] = { 0 };
39 sprintf(path, "/proc/%d/maps", pid);
40 FILE* fp = fopen(path, "r");
41 if (fp == NULL) {
42 return 0;
43 }
44
45 char buffer[MAX_BUF] = { 0 };
46 while (fgets(buffer, MAX_BUF, fp) != NULL) {
47 if (strstr(buffer, so) != NULL) {
48 fclose(fp);
49 return 1;
50 }
51 }
52 fclose(fp);
53 return 0;
54 }
55
CallBack001(struct dl_phdr_info* info, size_t size, void* data)56 static int CallBack001(struct dl_phdr_info* info, size_t size, void* data)
57 {
58 if (strcmp(info->dlpi_name, g_libPath) != 0 || strcmp(info->dlpi_name, g_initlibPath) != 0) {
59 return 0;
60 }
61 test_value++;
62 if (test_value != 1) {
63 t_error("test_value should be 1, but: %d\n", test_value);
64 }
65 test_value--;
66
67 if (test_value != 0) {
68 t_error("test_value should be 0, but: %d\n", test_value);
69 }
70 return 0;
71 }
72
CallBack002(struct dl_phdr_info* info, size_t size, void* data)73 static int CallBack002(struct dl_phdr_info* info, size_t size, void* data)
74 {
75 if (strcmp(info->dlpi_name, g_libPath) != 0 || strcmp(info->dlpi_name, g_initlibPath) != 0) {
76 return 0;
77 }
78 test_value = test_value + 2;
79 if (test_value != 2) {
80 t_error("test_value should be 2, but: %d\n", test_value);
81 }
82
83 test_value = test_value - 2;
84 if (test_value != 0) {
85 t_error("test_value should be 0, but: %d\n", test_value);
86 }
87 return 0;
88 }
89
CallBack003(void* arg)90 static void* CallBack003(void* arg)
91 {
92 pid_t pid = fork();
93 if (pid > 0) {
94 int status = 0;
95 int options = 0;
96 pid_t waitpid_for_pind = waitpid(pid, &status, options);
97 if (waitpid_for_pind != pid) {
98 t_error("%s waitpid get pid is %d are not want %d\n", __func__, waitpid_for_pind, pid);
99 }
100 if (status != 0) {
101 t_error("%s waitpid get status is %d are not 0\n", __func__, status);
102 }
103 } else if (pid == 0) {
104 sleep(1);
105 exit(0);
106 } else {
107 t_error("%s waitpid fork error\n");
108 }
109 return arg;
110 }
111
CallBack004(struct dl_phdr_info* info, size_t size, void* data)112 static int CallBack004(struct dl_phdr_info* info, size_t size, void* data)
113 {
114 char *memory = (char *)malloc(TEST_SIZE);
115 free(memory);
116 return 0;
117 }
118
dlopen_dlclose_test001(void* arg)119 static void* dlopen_dlclose_test001(void* arg)
120 {
121 void* handle = dlopen(g_libPath, RTLD_NOW);
122 if (!handle) {
123 t_error("dlopen(name=%s, mode=%d) failed: %s\n", g_libPath, RTLD_NOW, dlerror());
124 }
125 dlclose(handle);
126 return arg;
127 }
128
dlopen_dlclose_test002(void* arg)129 static void* dlopen_dlclose_test002(void* arg)
130 {
131 void* handle = dlopen(g_initlibPath, RTLD_NOW);
132 if (!handle) {
133 t_error("dlopen(name=%s, mode=%d) failed: %s\n", g_initlibPath, RTLD_NOW, dlerror());
134 }
135 g_init_handler = handle;
136 return arg;
137 }
138
dlopen_dlclose_test003(void* arg)139 static void* dlopen_dlclose_test003(void* arg)
140 {
141 dlclose(g_init_handler);
142 return arg;
143 }
144
dlopen_dlclose_test0041(void* arg)145 static void* dlopen_dlclose_test0041(void* arg)
146 {
147 dl_iterate_phdr(CallBack001, NULL);
148 return arg;
149 }
150
dlopen_dlclose_test0042(void* arg)151 static void* dlopen_dlclose_test0042(void* arg)
152 {
153 dl_iterate_phdr(CallBack002, NULL);
154 return arg;
155 }
156
dlopen_dlclose_test005(void* arg)157 static void* dlopen_dlclose_test005(void* arg)
158 {
159 int(* get_val)(void) = dlsym(g_init_handler, "getVal");
160 if (get_val == NULL) {
161 t_error("dlsym failed, don't find the symbol getVal\n");
162 }
163 if (get_val != NULL && get_val() != 1) {
164 t_error("This val after init should be 1, but %d\n", get_val());
165 }
166 return arg;
167 }
168
dlopen_dlclose_test006(void* arg)169 static void* dlopen_dlclose_test006(void* arg)
170 {
171 dl_iterate_phdr(CallBack004, NULL);
172 return arg;
173 }
174
do_test_concurrently(void *(*test) (void *arg), size_t num_threads)175 static void do_test_concurrently(void *(*test) (void *arg), size_t num_threads)
176 {
177 pthread_t *threads = (pthread_t *) malloc(sizeof(pthread_t) * num_threads);
178 if (threads == NULL) {
179 t_error("Failed to allocate memory: %s\n", strerror(errno));
180 return;
181 }
182
183 size_t last = 0;
184 while (last < num_threads) {
185 if (pthread_create(&(threads[last]), NULL, test, NULL)) {
186 t_error("Failed to create thread: %s\n", strerror(errno));
187 break;
188 }
189 last++;
190 }
191
192 for (size_t i = 0; i < last; i++) {
193 if (pthread_join(threads[i], NULL)) {
194 t_error("Failed to join thread: %s\n", strerror(errno));
195 }
196 }
197
198 free(threads);
199 return;
200 }
201
do_test_double_concurrently(void *(*test1) (void *arg), void *(*test2) (void *arg), size_t num_threads1, size_t num_threads2)202 static void do_test_double_concurrently(void *(*test1) (void *arg), void *(*test2) (void *arg),
203 size_t num_threads1, size_t num_threads2)
204 {
205 pthread_t *threads = (pthread_t *) malloc(sizeof(pthread_t) * (num_threads1 + num_threads2));
206 if (threads == NULL) {
207 t_error("Failed to allocate memory: %s\n", strerror(errno));
208 return;
209 }
210
211 size_t last = 0;
212 while (last < num_threads1) {
213 if (pthread_create(&(threads[last]), NULL, test1, NULL)) {
214 t_error("Failed to create thread: %s\n", strerror(errno));
215 break;
216 }
217 last++;
218 }
219
220 while (last < num_threads1 + num_threads2) {
221 if (pthread_create(&(threads[last]), NULL, test2, NULL)) {
222 t_error("Failed to create thread: %s\n", strerror(errno));
223 break;
224 }
225 last++;
226 }
227
228 for (size_t i = 0; i < last; i++) {
229 if (pthread_join(threads[i], NULL)) {
230 t_error("Failed to join thread: %s\n", strerror(errno));
231 }
232 }
233
234 free(threads);
235 return;
236 }
237
238 /**
239 * @tc.name : dl_multithread_lock_0100
240 * @tc.desc : multithreaded dlopen/dlclose, at the end the expected so file should not be in memory.
241 * @tc.level : Level 0
242 */
dl_multithread_lock_0100(void)243 void dl_multithread_lock_0100(void)
244 {
245 size_t num_threads = 1000;
246 do_test_concurrently(dlopen_dlclose_test001, num_threads);
247 if (check_loaded((char*)g_libPath)) {
248 t_error("This so file should not exist, %s\n", (char*)g_libPath);
249 }
250 }
251
252 /**
253 * @tc.name : dl_multithread_lock_0200
254 * @tc.desc : multithreaded dlopen, the init constructor should be called only once.
255 * @tc.level : Level 0
256 */
dl_multithread_lock_0200(void)257 void dl_multithread_lock_0200(void)
258 {
259 size_t num_threads = 20;
260 do_test_concurrently(dlopen_dlclose_test002, num_threads);
261 if (!check_loaded((char*)g_initlibPath)) {
262 t_error("This so file should exist, %s\n", (char*)g_initlibPath);
263 }
264 int(* get_val)(void) = dlsym(g_init_handler, "getVal");
265 if (get_val == NULL) {
266 t_error("dlsym failed, don't find the symbol getVal\n");
267 }
268 if (get_val != NULL && get_val() != 1) {
269 t_error("This val after init should be 1, but %d\n", get_val());
270 }
271 }
272
273 /**
274 * @tc.name : dl_multithread_lock_0300
275 * @tc.desc : multithreaded dlopen, the deconstructor should be called only at the last dlclose.
276 * @tc.level : Level 0
277 */
dl_multithread_lock_0300(void)278 void dl_multithread_lock_0300(void)
279 {
280 size_t num_threads = 19;
281 do_test_concurrently(dlopen_dlclose_test003, num_threads);
282 if (!check_loaded((char*)g_initlibPath)) {
283 t_error("This so file should exist, %s\n", (char*)g_initlibPath);
284 }
285 int(* get_val)(void) = dlsym(g_init_handler, "getVal");
286 if (get_val == NULL) {
287 t_error("dlsym failed, don't find the symbol getVal\n");
288 }
289 if (get_val != NULL && get_val() != 1) {
290 t_error("This val after init should be 1, but %d\n", get_val());
291 }
292 dlclose(g_init_handler);
293 if (check_loaded((char*)g_initlibPath)) {
294 t_error("This so file should not exist, %s\n", (char*)g_initlibPath);
295 }
296 }
297
298 /**
299 * @tc.name : dl_multithread_lock_0400
300 * @tc.desc : multithreaded iterate Callback in dl_iterate_phdr, the static test_value should be thread safe.
301 * @tc.level : Level 0
302 */
dl_multithread_lock_0400(void)303 void dl_multithread_lock_0400(void)
304 {
305 void* handle1 = dlopen(g_libPath, RTLD_NOW);
306 if (!handle1) {
307 t_error("dlopen(name=%s, mode=%d) failed: %s\n", g_libPath, RTLD_NOW, dlerror());
308 }
309
310 void* handle2 = dlopen(g_initlibPath, RTLD_NOW);
311 if (!handle2) {
312 t_error("dlopen(name=%s, mode=%d) failed: %s\n", g_initlibPath, RTLD_NOW, dlerror());
313 }
314
315 size_t num_threads = 5;
316 do_test_double_concurrently(dlopen_dlclose_test0041, dlopen_dlclose_test0042, num_threads, num_threads);
317
318 dlclose(handle1);
319 dlclose(handle2);
320 if (check_loaded((char*)g_initlibPath) || check_loaded((char*)g_libPath)) {
321 t_error("These so files should not exist\n");
322 }
323 }
324
325 /**
326 * @tc.name : dl_multithread_lock_0500
327 * @tc.desc : multithreaded dlsym, dlsym should not be blocked by dlsym in other threads.
328 * @tc.level : Level 0
329 */
dl_multithread_lock_0500(void)330 void dl_multithread_lock_0500(void)
331 {
332 void* handle = dlopen(g_initlibPath, RTLD_NOW);
333 if (!handle) {
334 t_error("dlopen(name=%s, mode=%d) failed: %s\n", g_initlibPath, RTLD_NOW, dlerror());
335 }
336
337 g_init_handler = handle;
338 size_t num_threads = 1000;
339 do_test_concurrently(dlopen_dlclose_test005, num_threads);
340 g_init_handler = NULL;
341 dlclose(handle);
342 }
343
344 /**
345 * @tc.name : dl_multithread_lock_0600
346 * @tc.desc : malloc and fork in different threads to check that there is no ABBA deadlock
347 * (ld lock and jemalloc lock).
348 * @tc.level : Level 0
349 */
dl_multithread_lock_0600(void)350 void dl_multithread_lock_0600(void)
351 {
352 size_t num_threads = 100;
353 do_test_double_concurrently(dlopen_dlclose_test006, CallBack003, num_threads, num_threads);
354 }
355
main(int argc, char* argv[])356 int main(int argc, char* argv[])
357 {
358 dl_multithread_lock_0100();
359 dl_multithread_lock_0200();
360 dl_multithread_lock_0300();
361 dl_multithread_lock_0400();
362 dl_multithread_lock_0500();
363 dl_multithread_lock_0600();
364 return t_status;
365 }