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