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 }