1570af302Sopenharmony_ci/*
2570af302Sopenharmony_ci * Copyright (C) 2024 Huawei Device Co., Ltd.
3570af302Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
4570af302Sopenharmony_ci * you may not use this file except in compliance with the License.
5570af302Sopenharmony_ci * You may obtain a copy of the License at
6570af302Sopenharmony_ci *
7570af302Sopenharmony_ci *	http://www.apache.org/licenses/LICENSE-2.0
8570af302Sopenharmony_ci *
9570af302Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
10570af302Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
11570af302Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12570af302Sopenharmony_ci * See the License for the specific language governing permissions and
13570af302Sopenharmony_ci * limitations under the License.
14570af302Sopenharmony_ci */
15570af302Sopenharmony_ci#include <dlfcn.h>
16570af302Sopenharmony_ci#include <pthread.h>
17570af302Sopenharmony_ci#include <stdio.h>
18570af302Sopenharmony_ci#include <string.h>
19570af302Sopenharmony_ci#include <stdlib.h>
20570af302Sopenharmony_ci#include <errno.h>
21570af302Sopenharmony_ci#include <link.h>
22570af302Sopenharmony_ci#include <sys/wait.h>
23570af302Sopenharmony_ci
24570af302Sopenharmony_ci#include "test.h"
25570af302Sopenharmony_ci
26570af302Sopenharmony_ci#define MAX_BUF 256
27570af302Sopenharmony_ci#define TEST_NUM 1000
28570af302Sopenharmony_ci#define TEST_SIZE 4096
29570af302Sopenharmony_ci
30570af302Sopenharmony_ciconst char* g_libPath = "/data/local/tmp/libc-test-lib/libdlopen_dso.so";
31570af302Sopenharmony_ciconst char* g_initlibPath = "/data/local/tmp/libc-test-lib/libdlopen_init.so";
32570af302Sopenharmony_civoid* g_init_handler = NULL;
33570af302Sopenharmony_cistatic int test_value = 0;
34570af302Sopenharmony_ci
35570af302Sopenharmony_ciint check_loaded(char* so)
36570af302Sopenharmony_ci{
37570af302Sopenharmony_ci    int pid = getpid();
38570af302Sopenharmony_ci    char path[MAX_BUF] = { 0 };
39570af302Sopenharmony_ci    sprintf(path, "/proc/%d/maps", pid);
40570af302Sopenharmony_ci    FILE* fp = fopen(path, "r");
41570af302Sopenharmony_ci    if (fp == NULL) {
42570af302Sopenharmony_ci        return 0;
43570af302Sopenharmony_ci    }
44570af302Sopenharmony_ci
45570af302Sopenharmony_ci    char buffer[MAX_BUF] = { 0 };
46570af302Sopenharmony_ci    while (fgets(buffer, MAX_BUF, fp) != NULL) {
47570af302Sopenharmony_ci        if (strstr(buffer, so) != NULL) {
48570af302Sopenharmony_ci            fclose(fp);
49570af302Sopenharmony_ci            return 1;
50570af302Sopenharmony_ci        }
51570af302Sopenharmony_ci    }
52570af302Sopenharmony_ci    fclose(fp);
53570af302Sopenharmony_ci    return 0;
54570af302Sopenharmony_ci}
55570af302Sopenharmony_ci
56570af302Sopenharmony_cistatic int CallBack001(struct dl_phdr_info* info, size_t size, void* data)
57570af302Sopenharmony_ci{
58570af302Sopenharmony_ci    if (strcmp(info->dlpi_name, g_libPath) != 0 || strcmp(info->dlpi_name, g_initlibPath) != 0) {
59570af302Sopenharmony_ci        return 0;
60570af302Sopenharmony_ci    }
61570af302Sopenharmony_ci    test_value++;
62570af302Sopenharmony_ci    if (test_value != 1) {
63570af302Sopenharmony_ci        t_error("test_value should be 1, but: %d\n", test_value);
64570af302Sopenharmony_ci    }
65570af302Sopenharmony_ci    test_value--;
66570af302Sopenharmony_ci
67570af302Sopenharmony_ci    if (test_value != 0) {
68570af302Sopenharmony_ci        t_error("test_value should be 0, but: %d\n", test_value);
69570af302Sopenharmony_ci    }
70570af302Sopenharmony_ci    return 0;
71570af302Sopenharmony_ci}
72570af302Sopenharmony_ci
73570af302Sopenharmony_cistatic int CallBack002(struct dl_phdr_info* info, size_t size, void* data)
74570af302Sopenharmony_ci{
75570af302Sopenharmony_ci    if (strcmp(info->dlpi_name, g_libPath) != 0 || strcmp(info->dlpi_name, g_initlibPath) != 0) {
76570af302Sopenharmony_ci        return 0;
77570af302Sopenharmony_ci    }
78570af302Sopenharmony_ci    test_value = test_value + 2;
79570af302Sopenharmony_ci    if (test_value != 2) {
80570af302Sopenharmony_ci        t_error("test_value should be 2, but: %d\n", test_value);
81570af302Sopenharmony_ci    }
82570af302Sopenharmony_ci
83570af302Sopenharmony_ci    test_value = test_value - 2;
84570af302Sopenharmony_ci    if (test_value != 0) {
85570af302Sopenharmony_ci        t_error("test_value should be 0, but: %d\n", test_value);
86570af302Sopenharmony_ci    }
87570af302Sopenharmony_ci    return 0;
88570af302Sopenharmony_ci}
89570af302Sopenharmony_ci
90570af302Sopenharmony_cistatic void* CallBack003(void* arg)
91570af302Sopenharmony_ci{
92570af302Sopenharmony_ci    pid_t pid = fork();
93570af302Sopenharmony_ci    if (pid > 0) {
94570af302Sopenharmony_ci        int status = 0;
95570af302Sopenharmony_ci        int options = 0;
96570af302Sopenharmony_ci        pid_t waitpid_for_pind = waitpid(pid, &status, options);
97570af302Sopenharmony_ci        if (waitpid_for_pind != pid) {
98570af302Sopenharmony_ci            t_error("%s waitpid get pid is %d are not want %d\n", __func__, waitpid_for_pind, pid);
99570af302Sopenharmony_ci        }
100570af302Sopenharmony_ci        if (status != 0) {
101570af302Sopenharmony_ci            t_error("%s waitpid get status is %d are not 0\n", __func__, status);
102570af302Sopenharmony_ci        }
103570af302Sopenharmony_ci    } else if (pid == 0) {
104570af302Sopenharmony_ci        sleep(1);
105570af302Sopenharmony_ci        exit(0);
106570af302Sopenharmony_ci    } else {
107570af302Sopenharmony_ci        t_error("%s waitpid fork error\n");
108570af302Sopenharmony_ci    }
109570af302Sopenharmony_ci    return arg;
110570af302Sopenharmony_ci}
111570af302Sopenharmony_ci
112570af302Sopenharmony_cistatic int CallBack004(struct dl_phdr_info* info, size_t size, void* data)
113570af302Sopenharmony_ci{
114570af302Sopenharmony_ci    char *memory = (char *)malloc(TEST_SIZE);
115570af302Sopenharmony_ci    free(memory);
116570af302Sopenharmony_ci    return 0;
117570af302Sopenharmony_ci}
118570af302Sopenharmony_ci
119570af302Sopenharmony_cistatic void* dlopen_dlclose_test001(void* arg)
120570af302Sopenharmony_ci{
121570af302Sopenharmony_ci    void* handle = dlopen(g_libPath, RTLD_NOW);
122570af302Sopenharmony_ci    if (!handle) {
123570af302Sopenharmony_ci      t_error("dlopen(name=%s, mode=%d) failed: %s\n", g_libPath, RTLD_NOW, dlerror());
124570af302Sopenharmony_ci    }
125570af302Sopenharmony_ci    dlclose(handle);
126570af302Sopenharmony_ci    return arg;
127570af302Sopenharmony_ci}
128570af302Sopenharmony_ci
129570af302Sopenharmony_cistatic void* dlopen_dlclose_test002(void* arg)
130570af302Sopenharmony_ci{
131570af302Sopenharmony_ci    void* handle = dlopen(g_initlibPath, RTLD_NOW);
132570af302Sopenharmony_ci    if (!handle) {
133570af302Sopenharmony_ci      t_error("dlopen(name=%s, mode=%d) failed: %s\n", g_initlibPath, RTLD_NOW, dlerror());
134570af302Sopenharmony_ci    }
135570af302Sopenharmony_ci    g_init_handler = handle;
136570af302Sopenharmony_ci    return arg;
137570af302Sopenharmony_ci}
138570af302Sopenharmony_ci
139570af302Sopenharmony_cistatic void* dlopen_dlclose_test003(void* arg)
140570af302Sopenharmony_ci{
141570af302Sopenharmony_ci    dlclose(g_init_handler);
142570af302Sopenharmony_ci    return arg;
143570af302Sopenharmony_ci}
144570af302Sopenharmony_ci
145570af302Sopenharmony_cistatic void* dlopen_dlclose_test0041(void* arg)
146570af302Sopenharmony_ci{
147570af302Sopenharmony_ci    dl_iterate_phdr(CallBack001, NULL);
148570af302Sopenharmony_ci    return arg;
149570af302Sopenharmony_ci}
150570af302Sopenharmony_ci
151570af302Sopenharmony_cistatic void* dlopen_dlclose_test0042(void* arg)
152570af302Sopenharmony_ci{
153570af302Sopenharmony_ci    dl_iterate_phdr(CallBack002, NULL);
154570af302Sopenharmony_ci    return arg;
155570af302Sopenharmony_ci}
156570af302Sopenharmony_ci
157570af302Sopenharmony_cistatic void* dlopen_dlclose_test005(void* arg)
158570af302Sopenharmony_ci{
159570af302Sopenharmony_ci    int(* get_val)(void) = dlsym(g_init_handler, "getVal");
160570af302Sopenharmony_ci    if (get_val == NULL) {
161570af302Sopenharmony_ci        t_error("dlsym failed, don't find the symbol getVal\n");
162570af302Sopenharmony_ci    }
163570af302Sopenharmony_ci    if (get_val != NULL && get_val() != 1) {
164570af302Sopenharmony_ci        t_error("This val after init should be 1, but %d\n", get_val());
165570af302Sopenharmony_ci    }
166570af302Sopenharmony_ci    return arg;
167570af302Sopenharmony_ci}
168570af302Sopenharmony_ci
169570af302Sopenharmony_cistatic void* dlopen_dlclose_test006(void* arg)
170570af302Sopenharmony_ci{
171570af302Sopenharmony_ci    dl_iterate_phdr(CallBack004, NULL);
172570af302Sopenharmony_ci    return arg;
173570af302Sopenharmony_ci}
174570af302Sopenharmony_ci
175570af302Sopenharmony_cistatic void do_test_concurrently(void *(*test) (void *arg), size_t num_threads)
176570af302Sopenharmony_ci{
177570af302Sopenharmony_ci    pthread_t *threads = (pthread_t *) malloc(sizeof(pthread_t) * num_threads);
178570af302Sopenharmony_ci    if (threads == NULL) {
179570af302Sopenharmony_ci        t_error("Failed to allocate memory: %s\n", strerror(errno));
180570af302Sopenharmony_ci        return;
181570af302Sopenharmony_ci    }
182570af302Sopenharmony_ci
183570af302Sopenharmony_ci    size_t last = 0;
184570af302Sopenharmony_ci    while (last < num_threads) {
185570af302Sopenharmony_ci        if (pthread_create(&(threads[last]), NULL, test, NULL)) {
186570af302Sopenharmony_ci            t_error("Failed to create thread: %s\n", strerror(errno));
187570af302Sopenharmony_ci            break;
188570af302Sopenharmony_ci        }
189570af302Sopenharmony_ci        last++;
190570af302Sopenharmony_ci    }
191570af302Sopenharmony_ci
192570af302Sopenharmony_ci    for (size_t i = 0; i < last; i++) {
193570af302Sopenharmony_ci        if (pthread_join(threads[i], NULL)) {
194570af302Sopenharmony_ci            t_error("Failed to join thread: %s\n", strerror(errno));
195570af302Sopenharmony_ci        }
196570af302Sopenharmony_ci    }
197570af302Sopenharmony_ci
198570af302Sopenharmony_ci    free(threads);
199570af302Sopenharmony_ci    return;
200570af302Sopenharmony_ci}
201570af302Sopenharmony_ci
202570af302Sopenharmony_cistatic void do_test_double_concurrently(void *(*test1) (void *arg), void *(*test2) (void *arg),
203570af302Sopenharmony_ci    size_t num_threads1, size_t num_threads2)
204570af302Sopenharmony_ci{
205570af302Sopenharmony_ci    pthread_t *threads = (pthread_t *) malloc(sizeof(pthread_t) * (num_threads1 + num_threads2));
206570af302Sopenharmony_ci    if (threads == NULL) {
207570af302Sopenharmony_ci        t_error("Failed to allocate memory: %s\n", strerror(errno));
208570af302Sopenharmony_ci        return;
209570af302Sopenharmony_ci    }
210570af302Sopenharmony_ci
211570af302Sopenharmony_ci    size_t last = 0;
212570af302Sopenharmony_ci    while (last < num_threads1) {
213570af302Sopenharmony_ci        if (pthread_create(&(threads[last]), NULL, test1, NULL)) {
214570af302Sopenharmony_ci            t_error("Failed to create thread: %s\n", strerror(errno));
215570af302Sopenharmony_ci            break;
216570af302Sopenharmony_ci        }
217570af302Sopenharmony_ci        last++;
218570af302Sopenharmony_ci    }
219570af302Sopenharmony_ci
220570af302Sopenharmony_ci    while (last < num_threads1 + num_threads2) {
221570af302Sopenharmony_ci        if (pthread_create(&(threads[last]), NULL, test2, NULL)) {
222570af302Sopenharmony_ci            t_error("Failed to create thread: %s\n", strerror(errno));
223570af302Sopenharmony_ci            break;
224570af302Sopenharmony_ci        }
225570af302Sopenharmony_ci        last++;
226570af302Sopenharmony_ci    }
227570af302Sopenharmony_ci
228570af302Sopenharmony_ci    for (size_t i = 0; i < last; i++) {
229570af302Sopenharmony_ci        if (pthread_join(threads[i], NULL)) {
230570af302Sopenharmony_ci            t_error("Failed to join thread: %s\n", strerror(errno));
231570af302Sopenharmony_ci        }
232570af302Sopenharmony_ci    }
233570af302Sopenharmony_ci
234570af302Sopenharmony_ci    free(threads);
235570af302Sopenharmony_ci    return;
236570af302Sopenharmony_ci}
237570af302Sopenharmony_ci
238570af302Sopenharmony_ci/**
239570af302Sopenharmony_ci * @tc.name      : dl_multithread_lock_0100
240570af302Sopenharmony_ci * @tc.desc      : multithreaded dlopen/dlclose, at the end the expected so file should not be in memory.
241570af302Sopenharmony_ci * @tc.level     : Level 0
242570af302Sopenharmony_ci */
243570af302Sopenharmony_civoid dl_multithread_lock_0100(void)
244570af302Sopenharmony_ci{
245570af302Sopenharmony_ci    size_t num_threads = 1000;
246570af302Sopenharmony_ci    do_test_concurrently(dlopen_dlclose_test001, num_threads);
247570af302Sopenharmony_ci    if (check_loaded((char*)g_libPath)) {
248570af302Sopenharmony_ci        t_error("This so file should not exist, %s\n", (char*)g_libPath);
249570af302Sopenharmony_ci    }
250570af302Sopenharmony_ci}
251570af302Sopenharmony_ci
252570af302Sopenharmony_ci/**
253570af302Sopenharmony_ci * @tc.name      : dl_multithread_lock_0200
254570af302Sopenharmony_ci * @tc.desc      : multithreaded dlopen, the init constructor should be called only once.
255570af302Sopenharmony_ci * @tc.level     : Level 0
256570af302Sopenharmony_ci */
257570af302Sopenharmony_civoid dl_multithread_lock_0200(void)
258570af302Sopenharmony_ci{
259570af302Sopenharmony_ci    size_t num_threads = 20;
260570af302Sopenharmony_ci    do_test_concurrently(dlopen_dlclose_test002, num_threads);
261570af302Sopenharmony_ci    if (!check_loaded((char*)g_initlibPath)) {
262570af302Sopenharmony_ci        t_error("This so file should exist, %s\n", (char*)g_initlibPath);
263570af302Sopenharmony_ci    }
264570af302Sopenharmony_ci    int(* get_val)(void) = dlsym(g_init_handler, "getVal");
265570af302Sopenharmony_ci    if (get_val == NULL) {
266570af302Sopenharmony_ci        t_error("dlsym failed, don't find the symbol getVal\n");
267570af302Sopenharmony_ci    }
268570af302Sopenharmony_ci    if (get_val != NULL && get_val() != 1) {
269570af302Sopenharmony_ci        t_error("This val after init should be 1, but %d\n", get_val());
270570af302Sopenharmony_ci    }
271570af302Sopenharmony_ci}
272570af302Sopenharmony_ci
273570af302Sopenharmony_ci/**
274570af302Sopenharmony_ci * @tc.name      : dl_multithread_lock_0300
275570af302Sopenharmony_ci * @tc.desc      : multithreaded dlopen, the deconstructor should be called only at the last dlclose.
276570af302Sopenharmony_ci * @tc.level     : Level 0
277570af302Sopenharmony_ci */
278570af302Sopenharmony_civoid dl_multithread_lock_0300(void)
279570af302Sopenharmony_ci{
280570af302Sopenharmony_ci    size_t num_threads = 19;
281570af302Sopenharmony_ci    do_test_concurrently(dlopen_dlclose_test003, num_threads);
282570af302Sopenharmony_ci    if (!check_loaded((char*)g_initlibPath)) {
283570af302Sopenharmony_ci        t_error("This so file should exist, %s\n", (char*)g_initlibPath);
284570af302Sopenharmony_ci    }
285570af302Sopenharmony_ci    int(* get_val)(void) = dlsym(g_init_handler, "getVal");
286570af302Sopenharmony_ci    if (get_val == NULL) {
287570af302Sopenharmony_ci        t_error("dlsym failed, don't find the symbol getVal\n");
288570af302Sopenharmony_ci    }
289570af302Sopenharmony_ci    if (get_val != NULL && get_val() != 1) {
290570af302Sopenharmony_ci        t_error("This val after init should be 1, but %d\n", get_val());
291570af302Sopenharmony_ci    }
292570af302Sopenharmony_ci    dlclose(g_init_handler);
293570af302Sopenharmony_ci    if (check_loaded((char*)g_initlibPath)) {
294570af302Sopenharmony_ci        t_error("This so file should not exist, %s\n", (char*)g_initlibPath);
295570af302Sopenharmony_ci    }
296570af302Sopenharmony_ci}
297570af302Sopenharmony_ci
298570af302Sopenharmony_ci/**
299570af302Sopenharmony_ci * @tc.name      : dl_multithread_lock_0400
300570af302Sopenharmony_ci * @tc.desc      : multithreaded iterate Callback in dl_iterate_phdr, the static test_value should be thread safe.
301570af302Sopenharmony_ci * @tc.level     : Level 0
302570af302Sopenharmony_ci */
303570af302Sopenharmony_civoid dl_multithread_lock_0400(void)
304570af302Sopenharmony_ci{
305570af302Sopenharmony_ci    void* handle1 = dlopen(g_libPath, RTLD_NOW);
306570af302Sopenharmony_ci    if (!handle1) {
307570af302Sopenharmony_ci        t_error("dlopen(name=%s, mode=%d) failed: %s\n", g_libPath, RTLD_NOW, dlerror());
308570af302Sopenharmony_ci    }
309570af302Sopenharmony_ci
310570af302Sopenharmony_ci    void* handle2 = dlopen(g_initlibPath, RTLD_NOW);
311570af302Sopenharmony_ci    if (!handle2) {
312570af302Sopenharmony_ci        t_error("dlopen(name=%s, mode=%d) failed: %s\n", g_initlibPath, RTLD_NOW, dlerror());
313570af302Sopenharmony_ci    }
314570af302Sopenharmony_ci
315570af302Sopenharmony_ci    size_t num_threads = 5;
316570af302Sopenharmony_ci    do_test_double_concurrently(dlopen_dlclose_test0041, dlopen_dlclose_test0042, num_threads, num_threads);
317570af302Sopenharmony_ci
318570af302Sopenharmony_ci    dlclose(handle1);
319570af302Sopenharmony_ci    dlclose(handle2);
320570af302Sopenharmony_ci    if (check_loaded((char*)g_initlibPath) || check_loaded((char*)g_libPath)) {
321570af302Sopenharmony_ci        t_error("These so files should not exist\n");
322570af302Sopenharmony_ci    }
323570af302Sopenharmony_ci}
324570af302Sopenharmony_ci
325570af302Sopenharmony_ci/**
326570af302Sopenharmony_ci * @tc.name      : dl_multithread_lock_0500
327570af302Sopenharmony_ci * @tc.desc      : multithreaded dlsym, dlsym should not be blocked by dlsym in other threads.
328570af302Sopenharmony_ci * @tc.level     : Level 0
329570af302Sopenharmony_ci */
330570af302Sopenharmony_civoid dl_multithread_lock_0500(void)
331570af302Sopenharmony_ci{
332570af302Sopenharmony_ci    void* handle = dlopen(g_initlibPath, RTLD_NOW);
333570af302Sopenharmony_ci    if (!handle) {
334570af302Sopenharmony_ci        t_error("dlopen(name=%s, mode=%d) failed: %s\n", g_initlibPath, RTLD_NOW, dlerror());
335570af302Sopenharmony_ci    }
336570af302Sopenharmony_ci
337570af302Sopenharmony_ci    g_init_handler = handle;
338570af302Sopenharmony_ci    size_t num_threads = 1000;
339570af302Sopenharmony_ci    do_test_concurrently(dlopen_dlclose_test005, num_threads);
340570af302Sopenharmony_ci    g_init_handler = NULL;
341570af302Sopenharmony_ci    dlclose(handle);
342570af302Sopenharmony_ci}
343570af302Sopenharmony_ci
344570af302Sopenharmony_ci/**
345570af302Sopenharmony_ci * @tc.name      : dl_multithread_lock_0600
346570af302Sopenharmony_ci * @tc.desc      : malloc and fork in different threads to check that there is no ABBA deadlock
347570af302Sopenharmony_ci *                (ld lock and jemalloc lock).
348570af302Sopenharmony_ci * @tc.level     : Level 0
349570af302Sopenharmony_ci */
350570af302Sopenharmony_civoid dl_multithread_lock_0600(void)
351570af302Sopenharmony_ci{
352570af302Sopenharmony_ci    size_t num_threads = 100;
353570af302Sopenharmony_ci    do_test_double_concurrently(dlopen_dlclose_test006, CallBack003, num_threads, num_threads);
354570af302Sopenharmony_ci}
355570af302Sopenharmony_ci
356570af302Sopenharmony_ciint main(int argc, char* argv[])
357570af302Sopenharmony_ci{
358570af302Sopenharmony_ci    dl_multithread_lock_0100();
359570af302Sopenharmony_ci    dl_multithread_lock_0200();
360570af302Sopenharmony_ci    dl_multithread_lock_0300();
361570af302Sopenharmony_ci    dl_multithread_lock_0400();
362570af302Sopenharmony_ci    dl_multithread_lock_0500();
363570af302Sopenharmony_ci    dl_multithread_lock_0600();
364570af302Sopenharmony_ci    return t_status;
365570af302Sopenharmony_ci}