1 /**
2 * Copyright (c) 2021-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
16 #include "file-inl.h"
17 #include "file_items.h"
18 #include "file_item_container.h"
19 #include "file_reader.h"
20 #include "utils/string_helpers.h"
21 #include "zip_archive.h"
22 #include "file.h"
23
24 #include "assembly-emitter.h"
25 #include "assembly-parser.h"
26
27 #include <cstdint>
28 #ifdef PANDA_TARGET_MOBILE
29 #include <unistd.h>
30 #endif
31
32 #include <vector>
33
34 #include <gtest/gtest.h>
35
36 namespace ark::panda_file::test {
37
GetPandaFile(std::vector<uint8_t> * data)38 static std::unique_ptr<const File> GetPandaFile(std::vector<uint8_t> *data)
39 {
40 os::mem::ConstBytePtr ptr(reinterpret_cast<std::byte *>(data->data()), data->size(),
41 [](std::byte *, size_t) noexcept {});
42 return File::OpenFromMemory(std::move(ptr));
43 }
44
GetEmptyPandaFileBytes()45 static std::vector<uint8_t> GetEmptyPandaFileBytes()
46 {
47 pandasm::Parser p;
48
49 auto source = R"()";
50
51 std::string srcFilename = "src.pa";
52 auto res = p.Parse(source, srcFilename);
53 ASSERT(p.ShowError().err == pandasm::Error::ErrorType::ERR_NONE);
54
55 auto pf = pandasm::AsmEmitter::Emit(res.Value());
56 ASSERT(pf != nullptr);
57
58 std::vector<uint8_t> data {};
59 const auto headerPtr = reinterpret_cast<const uint8_t *>(pf->GetHeader());
60 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
61 data.assign(headerPtr, headerPtr + sizeof(File::Header));
62
63 ASSERT(data.size() == sizeof(File::Header));
64
65 return data;
66 }
67
CreateOrAddZipPandaFile(std::vector<uint8_t> * data,const char * zipArchiveName,const char * filename,int append,int level)68 int CreateOrAddZipPandaFile(std::vector<uint8_t> *data, const char *zipArchiveName, const char *filename, int append,
69 int level)
70 {
71 return CreateOrAddFileIntoZip(zipArchiveName, filename, data, append, level);
72 }
73
CheckAnonMemoryName(const char * zipArchiveName)74 bool CheckAnonMemoryName([[maybe_unused]] const char *zipArchiveName)
75 {
76 // check if [annon:panda-classes.abc extracted in memory from /xx/__OpenPandaFileFromZip__.zip]
77 #ifdef PANDA_TARGET_MOBILE
78 bool result = false;
79 const char *prefix = "[anon:panda-";
80 int pid = getpid();
81 std::stringstream ss;
82 ss << "/proc/" << pid << "/maps";
83 std::ifstream f;
84 f.open(ss.str(), std::ios::in);
85 EXPECT_TRUE(f.is_open());
86 for (std::string line; std::getline(f, line);) {
87 if (line.find(prefix) != std::string::npos && line.find(zipArchiveName) != std::string::npos) {
88 result = true;
89 }
90 }
91 f.close();
92 return result;
93 #else
94 return true;
95 #endif
96 }
97
TEST(File,OpenMemory)98 TEST(File, OpenMemory)
99 {
100 {
101 auto data = GetEmptyPandaFileBytes();
102 auto ptr = GetPandaFile(&data);
103 EXPECT_NE(ptr, nullptr);
104 }
105
106 {
107 auto data = GetEmptyPandaFileBytes();
108 data[0] = 0x0; // Corrupt magic
109
110 auto ptr = GetPandaFile(&data);
111 EXPECT_EQ(ptr, nullptr);
112 }
113 }
114
TEST(File,GetClassByName)115 TEST(File, GetClassByName)
116 {
117 ItemContainer container;
118
119 std::vector<std::string> names = {"C", "B", "A"};
120 std::vector<ClassItem *> classes;
121 classes.reserve(names.size());
122
123 for (auto &name : names) {
124 classes.push_back(container.GetOrCreateClassItem(name));
125 }
126
127 MemoryWriter memWriter;
128
129 ASSERT_TRUE(container.Write(&memWriter));
130
131 // Read panda file from memory
132
133 auto data = memWriter.GetData();
134 auto pandaFile = GetPandaFile(&data);
135 ASSERT_NE(pandaFile, nullptr);
136
137 for (size_t i = 0; i < names.size(); i++) {
138 EXPECT_EQ(pandaFile->GetClassId(reinterpret_cast<const uint8_t *>(names[i].c_str())).GetOffset(),
139 classes[i]->GetOffset());
140 }
141 }
142
TEST(File,OpenPandaFile)143 TEST(File, OpenPandaFile)
144 {
145 // Create ZIP
146 auto data = GetEmptyPandaFileBytes();
147 int ret;
148 const char *zipFilename = "__OpenPandaFile__.zip";
149 const char *filename1 = ARCHIVE_FILENAME;
150 const char *filename2 = "classses2.abc"; // just for testing.
151 ret = CreateOrAddZipPandaFile(&data, zipFilename, filename1, APPEND_STATUS_CREATE, Z_BEST_COMPRESSION);
152 ASSERT_EQ(ret, 0);
153 ret = CreateOrAddZipPandaFile(&data, zipFilename, filename2, APPEND_STATUS_ADDINZIP, Z_BEST_COMPRESSION);
154 ASSERT_EQ(ret, 0);
155
156 // Open from ZIP
157 auto pf = OpenPandaFile(zipFilename);
158 EXPECT_NE(pf, nullptr);
159 EXPECT_STREQ((pf->GetFilename()).c_str(), zipFilename);
160 remove(zipFilename);
161 }
162
TEST(File,OpenPandaFileFromZipNameAnonMem)163 TEST(File, OpenPandaFileFromZipNameAnonMem)
164 {
165 // Create ZIP
166 auto data = GetEmptyPandaFileBytes();
167 int ret;
168 const char *zipFilename = "__OpenPandaFileFromZipNameAnonMem__.zip";
169 const char *filename1 = ARCHIVE_FILENAME;
170 ret = CreateOrAddZipPandaFile(&data, zipFilename, filename1, APPEND_STATUS_CREATE, Z_BEST_COMPRESSION);
171 ASSERT_EQ(ret, 0);
172
173 // Open from ZIP
174 auto pf = OpenPandaFile(zipFilename);
175 EXPECT_NE(pf, nullptr);
176 EXPECT_STREQ((pf->GetFilename()).c_str(), zipFilename);
177 ASSERT_TRUE(CheckAnonMemoryName(zipFilename));
178 remove(zipFilename);
179 }
180
TEST(File,OpenPandaFileOrZip)181 TEST(File, OpenPandaFileOrZip)
182 {
183 // Create ZIP
184 auto data = GetEmptyPandaFileBytes();
185 int ret;
186 const char *zipFilename = "__OpenPandaFileOrZip__.zip";
187 const char *filename1 = ARCHIVE_FILENAME;
188 const char *filename2 = "classes2.abc"; // just for testing.
189 ret = CreateOrAddZipPandaFile(&data, zipFilename, filename1, APPEND_STATUS_CREATE, Z_BEST_COMPRESSION);
190 ASSERT_EQ(ret, 0);
191 ret = CreateOrAddZipPandaFile(&data, zipFilename, filename2, APPEND_STATUS_ADDINZIP, Z_BEST_COMPRESSION);
192 ASSERT_EQ(ret, 0);
193
194 // Open from ZIP
195 auto pf = OpenPandaFileOrZip(zipFilename);
196 EXPECT_NE(pf, nullptr);
197 EXPECT_STREQ((pf->GetFilename()).c_str(), zipFilename);
198 remove(zipFilename);
199 }
200
TEST(File,OpenPandaFileUncompressed)201 TEST(File, OpenPandaFileUncompressed)
202 {
203 // Create ZIP
204 auto data = GetEmptyPandaFileBytes();
205 std::cout << "pandafile size = " << data.size() << std::endl;
206 int ret;
207 const char *zipFilename = "__OpenPandaFileUncompressed__.zip";
208 const char *filename1 = ARCHIVE_FILENAME;
209 const char *filename2 = "class.abc"; // just for testing.
210 ret = CreateOrAddZipPandaFile(&data, zipFilename, filename2, APPEND_STATUS_CREATE, Z_NO_COMPRESSION);
211 ASSERT_EQ(ret, 0);
212 ret = CreateOrAddZipPandaFile(&data, zipFilename, filename1, APPEND_STATUS_ADDINZIP, Z_NO_COMPRESSION);
213 ASSERT_EQ(ret, 0);
214
215 // Open from ZIP
216 auto pf = OpenPandaFileOrZip(zipFilename);
217 EXPECT_NE(pf, nullptr);
218 EXPECT_STREQ((pf->GetFilename()).c_str(), zipFilename);
219 remove(zipFilename);
220 }
221
TEST(File,LineNumberProgramDeduplication)222 TEST(File, LineNumberProgramDeduplication)
223 {
224 pandasm::Parser p;
225
226 auto source = R"delim(
227 .function void foo() {
228 return.void
229 }
230
231 .function void bar() {
232 return.void
233 }
234 )delim";
235
236 std::string srcFilename = "src.pa";
237 auto res = p.Parse(source, srcFilename);
238 ASSERT(p.ShowError().err == pandasm::Error::ErrorType::ERR_NONE);
239
240 ASSERT_EQ(res.Value().functionTable.size(), 2);
241 for (auto &a : res.Value().functionTable) {
242 ASSERT_TRUE(a.second.HasDebugInfo());
243 }
244
245 auto pf = pandasm::AsmEmitter::Emit(res.Value());
246 ASSERT(pf != nullptr);
247
248 auto reader = FileReader(std::move(pf));
249
250 ASSERT_TRUE(reader.ReadContainer());
251
252 int lnpCnt = 0;
253
254 ASSERT_NE(reader.GetItems()->size(), 0);
255
256 for (const auto &a : *reader.GetItems()) {
257 const auto typ = a.second->GetItemType();
258 if (typ == ItemTypes::LINE_NUMBER_PROGRAM_ITEM) {
259 lnpCnt++;
260 }
261 }
262
263 reader.GetContainerPtr()->ComputeLayout();
264 reader.GetContainerPtr()->DeduplicateCodeAndDebugInfo();
265
266 ASSERT_EQ(lnpCnt, 1);
267 }
268
269 } // namespace ark::panda_file::test
270