1/*
2 * Copyright (c) 2020-2021 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
16#include <dlfcn.h>
17#include <gtest/gtest.h>
18#include "log.h"
19#include "utils.h"
20#include "libfs.h"
21#include "KernelConstants.h"
22
23using namespace testing::ext;
24
25class DlopenTest : public testing::Test {
26};
27
28#define RES_DIR_DYLOAD RES_DIR_KERNEL "dyload/"
29#define DYLOAD_TEST_DIR "/storage/data/"
30
31/**
32 * @tc.number   SUB_KERNEL_DL_SO_0100
33 * @tc.name     the elf does not depend on any user-so, dlopen a full path so
34 * @tc.desc     [C- SOFTWARE -0200]
35 */
36HWTEST_F(DlopenTest, testDlopenFullPathSo, Function | MediumTest | Level1)
37{
38    char* resSO = RES_DIR_DYLOAD "libdso1.so";
39    char* newSO = DYLOAD_TEST_DIR "libdso1.so";
40
41    // test SetUp
42    ASSERT_EQ(CopyFile(resSO, newSO), 0);
43    LOG("SetUp ok");
44
45    // test in child process
46    pid_t pid = fork();
47    ASSERT_TRUE(pid >= 0) << "======== Fork Error! =========";
48    if (pid == 0) { // child
49        LOG("newSO: %s", newSO);
50        void* h = dlopen(newSO, RTLD_NOW);
51        if (!h) {
52            PANIC("dlopen %s failed: %s\n", newSO, dlerror());
53        }
54        LOG("dlopen '%s' ok", newSO);
55        int* i = (int*) dlsym(h, "g_int");
56        if (!i) {
57            PANIC("dlsym g_int failed: %s", dlerror());
58        }
59        if (*i != 1) {
60            PANIC("so initialization failed: want i=1 got i=%d", *i);
61        }
62
63        int (*f)(void) = (int (*)(void))dlsym(h, "inc_i");
64        if (!f) {
65            PANIC("dlsym func 'inc_i' failed: %s", dlerror());
66        }
67        f();
68        if (*i != 2) {
69            PANIC("inc_i call failed: want i=2 got i=%d", *i);
70        }
71        exit(0);
72    } else { // parent
73        Msleep(100);
74        WaitProcExitedOK(pid);
75    }
76
77    // test TearDown
78    ASSERT_EQ(RemoveFile(newSO), 0);
79    LOG("TearDown ok ");
80}
81
82/**
83 * @tc.number   SUB_KERNEL_DL_SO_0200
84 * @tc.name     the elf doesn't depend on any user-so, dlopen a relative-path so, twice
85 * @tc.desc     [C- SOFTWARE -0200]
86 */
87HWTEST_F(DlopenTest, testDlopenRelativePathSo, Function | MediumTest | Level1)
88{
89    char* resSO = RES_DIR_DYLOAD "libdso1.so";
90    char* testDir = DYLOAD_TEST_DIR "target";
91    char* testSo = DYLOAD_TEST_DIR "target/libdso1.so";
92    // test SetUp
93    char* curPath = GetCurrentPath();
94    ASSERT_NE(MakeDir(testDir), -1);
95    ASSERT_EQ(CopyFile(resSO, testSo), 0);
96    EXPECT_EQ(chdir(DYLOAD_TEST_DIR), 0);
97    LOG("SetUp ok");
98
99    // test in child process, so that this load-so will not effect other test
100    pid_t pid = fork();
101    ASSERT_TRUE(pid >= 0) << "======== Fork Error! =========";
102    if (pid == 0) { // child
103        void* h = dlopen("./target/libdso1.so", RTLD_NOW);
104        if (!h) {
105            PANIC("dlopen './target/libdso1.so' failed: %s", dlerror());
106        }
107        int* i = (int*) dlsym(h, "g_int");
108        if (!i) {
109            PANIC("dlsym g_int failed: %s", dlerror());
110        }
111        if (*i != 1) {
112            PANIC("so initialization failed: want i=1 got i=%d", *i);
113        }
114        if (dlclose(h)) {
115            PANIC("dlclose failed: %s", dlerror());
116        }
117
118        void* g = dlopen("../data/target/libdso1.so", RTLD_NOW);
119        if (!g) {
120            PANIC("dlopen so again failed: %s",  dlerror());
121        }
122        if (h != g) {
123            PANIC("dlopen same so return diff handle");
124        }
125        int (*f)(void) = (int (*)(void))dlsym(h, "inc_i");
126        if (!f) {
127            PANIC("dlsym func 'inc_i' failed: %s", dlerror());
128        }
129        f();
130        if (*i != 2) {
131            PANIC("inc_i call failed: want i=2 got i=%d", *i);
132        }
133        if (dlclose(h)) {
134            PANIC("dlclose failed: %s", dlerror());
135        }
136        LOG("Pass\n");
137        exit(0);
138    } else { // parent
139        Msleep(100);
140        WaitProcExitedOK(pid);
141    }
142
143    // test TearDown
144    EXPECT_EQ(chdir(curPath), 0);
145    ASSERT_EQ(RemoveFile(testSo), 0);
146    LOG("TearDown ok ");
147}
148
149/**
150 * @tc.number   SUB_KERNEL_DL_SO_0400
151 * @tc.name     the elf doesn't depend on any user-so. dlopen a not exist so.
152 * @tc.desc     [C- SOFTWARE -0200]
153 */
154HWTEST_F(DlopenTest, testDlopenNotExistSo, Function | MediumTest | Level3)
155{
156    dlerror(); // clear any old error message
157    char* errMsg = dlerror();
158    if (errMsg) {
159        LOG("dlerror should return NULL when called twice.");
160        FAIL();
161    }
162
163    void* h = dlopen("not_exist_dso1.so", RTLD_NOW);
164    if (h) {
165        LOG("dlopen a non-exist-so should return NULL!");
166        FAIL();
167    }
168    errMsg = dlerror();
169    if (!errMsg) {
170        LOG("dlerror return NULL!");
171        FAIL();
172    }
173    char* p = strcasestr(errMsg, "No such file");
174    if (!p) {
175        LOG("dlerror msg invalid, should include 'No such file', actual msg=%s", errMsg);
176        FAIL();
177    }
178}
179
180/**
181 * @tc.number   SUB_KERNEL_DL_SO_0500
182 * @tc.name     the elf doesn't depend on any user-so, and dlopen continuously loads a same so
183 * @tc.desc     [C- SOFTWARE -0200]
184 */
185HWTEST_F(DlopenTest, testDlopenSameSo, Function | MediumTest | Level2)
186{
187    char* resSO = RES_DIR_DYLOAD "libdso1.so";
188    char* newSO = DYLOAD_TEST_DIR "libdso1.so";
189
190    // test SetUp
191    ASSERT_EQ(CopyFile(resSO, newSO), 0);
192    LOG("SetUp ok");
193
194    // test in child process
195    pid_t pid = fork();
196    ASSERT_TRUE(pid >= 0) << "======== Fork Error! =========";
197    if (pid == 0) { // child
198        void* h1 = dlopen(newSO, RTLD_NOW);
199        if (!h1) {
200            PANIC("dlopen %s failed: %s\n", newSO, dlerror());
201        }
202        void* h2 = dlopen(newSO, RTLD_NOW);
203        if (!h2) {
204            PANIC("dlopen %s failed: %s\n", newSO, dlerror());
205        }
206        if (h1 != h2) {
207            PANIC("dlopen same so return diff handle");
208        }
209        if (dlclose(h1)) {
210            PANIC("dlclose failed: %s", dlerror());
211        }
212        if (dlclose(h2)) {
213            PANIC("dlclose second handle failed: %s", dlerror());
214        }
215        exit(0);
216    } else { // parent
217        Msleep(100);
218        WaitProcExitedOK(pid);
219    }
220
221    // test TearDown
222    ASSERT_EQ(RemoveFile(newSO), 0);
223    LOG("TearDown ok ");
224}
225
226/**
227 * @tc.number   SUB_KERNEL_DL_SO_0800
228 * @tc.name     The tested elf depends on full-rpath so, and test same-symbol behaviour.
229 *              and three so's have a same symbol
230 * @tc.desc     [C- SOFTWARE -0200]
231 */
232HWTEST_F(DlopenTest, testDlopenSameSymbolSo, Function | MediumTest | Level2)
233{
234    char* testELF = RES_DIR_DYLOAD "dyload_rpath_full";
235    char* resSO1  = RES_DIR_DYLOAD "libdso1.so";
236    char* resSO2  = RES_DIR_DYLOAD "libdso2.so";
237    char* resSO3  = RES_DIR_DYLOAD "libdso3.so";
238    char* newSO1 = DYLOAD_TEST_DIR "libdso1.so";
239    char* newSO2 = DYLOAD_TEST_DIR "libdso2.so";
240    char* newSO3 = DYLOAD_TEST_DIR "libdso3.so";
241
242    // test SetUp
243    EXPECT_EQ(CopyFile(resSO1, newSO1), 0);
244    EXPECT_EQ(CopyFile(resSO2, newSO2), 0);
245    EXPECT_EQ(CopyFile(resSO3, newSO3), 0);
246    LOG("SetUp ok");
247
248    // test
249    int rt = RunElf(testELF, NULL, NULL);
250    EXPECT_EQ(rt, 0) << "same-symbol test failed! exitcode=" << rt;
251
252    // test TearDown
253    EXPECT_EQ(RemoveFile(newSO1), 0);
254    EXPECT_EQ(RemoveFile(newSO2), 0);
255    EXPECT_EQ(RemoveFile(newSO3), 0);
256    LOG("TearDown ok ");
257}
258
259/**
260 * @tc.number   SUB_KERNEL_DL_SO_1000
261 * @tc.name     the test elf compiled with rpath, and the depended-so is in that path
262 * @tc.desc     [C- SOFTWARE -0200]
263 */
264HWTEST_F(DlopenTest, testDlopenSoInRelativeRpath, Function | MediumTest | Level2)
265{
266    char* testELF = RES_DIR_DYLOAD "dyload_rpath_relative";
267    char* resSO = RES_DIR_DYLOAD "libdso1.so";
268    char* newSO = "./lib/libdso1.so";
269
270    // test SetUp
271    ASSERT_NE(MakeDir("./lib"), -1);
272    ASSERT_EQ(CopyFile(resSO, newSO), 0);
273    LOG("SetUp ok");
274    Msleep(5000);
275
276    // test
277    int rt = RunElf(testELF, NULL, NULL);
278    EXPECT_EQ(rt, 0) << "dyload_rpath_relative failed! exitcode=" << rt;
279
280    // test TearDown
281    ASSERT_EQ(RemoveFile(newSO), 0);
282    LOG("TearDown ok ");
283}
284
285