• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2025 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_format_version.h"
17 #include "file-inl.h"
18 #include "os/file.h"
19 #include "os/mem.h"
20 #include "os/filesystem.h"
21 #include "mem/mem.h"
22 #include "panda_cache.h"
23 
24 #include "utils/hash.h"
25 #include "utils/logger.h"
26 #include "utils/utf.h"
27 #include "utils/span.h"
28 #include "zip_archive.h"
29 #include "trace/trace.h"
30 #include "securec.h"
31 
32 #include <cerrno>
33 #include <cstring>
34 
35 #include <algorithm>
36 #include <memory>
37 #include <string>
38 #include <variant>
39 #include <cstdio>
40 #include <map>
41 namespace ark::panda_file {
42 
43 // NOLINTNEXTLINE(readability-identifier-naming, modernize-avoid-c-arrays)
44 const char *ARCHIVE_FILENAME = "classes.abc";
45 // NOLINTNEXTLINE(readability-identifier-naming, modernize-avoid-c-arrays)
46 const char *ARCHIVE_SPLIT = "!/";
47 
48 const std::array<uint8_t, File::MAGIC_SIZE> File::MAGIC {'P', 'A', 'N', 'D', 'A', '\0', '\0', '\0'};
49 
50 // Name anonymous maps for perfing tools finding symbol file correctly.
51 // NOLINTNEXTLINE(readability-identifier-naming, modernize-avoid-c-arrays)
52 const char *ANONMAPNAME_PERFIX = "panda-";
53 
GetMode(panda_file::File::OpenMode openMode)54 os::file::Mode GetMode(panda_file::File::OpenMode openMode)
55 {
56     switch (openMode) {
57         case File::READ_ONLY: {
58             return os::file::Mode::READONLY;
59         }
60         case File::READ_WRITE: {
61 #ifdef PANDA_TARGET_WINDOWS
62             return os::file::Mode::READWRITE;
63 #else
64             return os::file::Mode::READONLY;
65 #endif
66         }
67         case File::WRITE_ONLY: {
68             return os::file::Mode::WRITEONLY;
69         }
70         default: {
71             break;
72         }
73     }
74 
75     UNREACHABLE();
76 }
77 
GetProt(panda_file::File::OpenMode mode)78 static uint32_t GetProt(panda_file::File::OpenMode mode)
79 {
80     uint32_t prot = os::mem::MMAP_PROT_READ;
81     if (mode == File::READ_WRITE) {
82         prot |= os::mem::MMAP_PROT_WRITE;
83     }
84     return prot;
85 }
86 
87 class AnonMemSet {
88 public:
89     using MemNameSet = std::map<std::string, std::string>;
90     using InsertResult = std::map<std::string, std::string>::iterator;
91 
GetInstance()92     static AnonMemSet &GetInstance()
93     {
94         static AnonMemSet anonMemSet;
95         return anonMemSet;
96     }
97 
Insert(const std::string & fileName,const std::string & anonMemName)98     InsertResult Insert(const std::string &fileName, const std::string &anonMemName)
99     {
100         return memNameSet_.emplace(fileName, anonMemName).first;
101     }
102 
Remove(const std::string & fileName)103     void Remove(const std::string &fileName)
104     {
105         auto it = memNameSet_.find(fileName);
106         if (it != memNameSet_.end()) {
107             memNameSet_.erase(it);
108         }
109     }
110 
111 private:
112     MemNameSet memNameSet_;
113 };
114 
OpenPandaFileOrZip(std::string_view location,panda_file::File::OpenMode openMode)115 std::unique_ptr<const File> OpenPandaFileOrZip(std::string_view location, panda_file::File::OpenMode openMode)
116 {
117     std::string_view archiveFilename = ARCHIVE_FILENAME;
118     std::size_t archiveSplitIndex = location.find(ARCHIVE_SPLIT);
119     if (archiveSplitIndex != std::string::npos) {
120         archiveFilename = location.substr(archiveSplitIndex + 2);  // 2 - archive split size
121         location = location.substr(0, archiveSplitIndex);
122     }
123 
124     return OpenPandaFile(location, archiveFilename, openMode);
125 }
126 
127 // NOLINTNEXTLINE(google-runtime-references)
OpenPandaFileFromZipErrorHandler(ZipArchiveHandle & handle)128 void OpenPandaFileFromZipErrorHandler(ZipArchiveHandle &handle)
129 {
130     if (handle != nullptr) {
131         if (ark::CloseArchiveFile(handle) != ZIPARCHIVE_OK) {
132             LOG(ERROR, PANDAFILE) << "CloseArchiveFile failed!";
133         }
134     }
135 }
136 
OpenPandaFileFromZipFile(ZipArchiveHandle & handle,std::string_view location,EntryFileStat & entry,std::string_view archiveName)137 std::unique_ptr<const panda_file::File> OpenPandaFileFromZipFile(ZipArchiveHandle &handle, std::string_view location,
138                                                                  EntryFileStat &entry, std::string_view archiveName)
139 {
140     uint32_t uncompressedLength = entry.GetUncompressedSize();
141     if (uncompressedLength == 0) {
142         LOG(ERROR, PANDAFILE) << "Panda file has zero length!";
143         return nullptr;
144     }
145 
146     size_t sizeToMmap = AlignUp(uncompressedLength, ark::os::mem::GetPageSize());
147     void *mem = os::mem::MapRWAnonymousRaw(sizeToMmap, false);
148     if (mem == nullptr) {
149         LOG(ERROR, PANDAFILE) << "Can't mmap anonymous!";
150         return nullptr;
151     }
152     os::mem::BytePtr ptr(reinterpret_cast<std::byte *>(mem), sizeToMmap, os::mem::MmapDeleter);
153     std::stringstream ss;
154     ss << ANONMAPNAME_PERFIX << archiveName << " extracted in memory from " << location;
155     auto it = AnonMemSet::GetInstance().Insert(std::string(location), ss.str());
156     auto ret = os::mem::TagAnonymousMemory(reinterpret_cast<void *>(ptr.Get()), sizeToMmap, it->second.c_str());
157     if (ret.has_value()) {
158         LOG(ERROR, PANDAFILE) << "Can't tag mmap anonymous!";
159         return nullptr;
160     }
161 
162     auto extractError = ExtractToMemory(handle, reinterpret_cast<uint8_t *>(ptr.Get()), sizeToMmap);
163     if (extractError != 0) {
164         LOG(ERROR, PANDAFILE) << "Can't extract!";
165         return nullptr;
166     }
167 
168     os::mem::ConstBytePtr constPtr = ptr.ToConst();
169     return panda_file::File::OpenFromMemory(std::move(constPtr), location);
170 }
171 
172 // CC-OFFNXT(G.FUN.01-CPP) solid logic
173 // NOLINTNEXTLINE(google-runtime-references)
HandleArchive(ZipArchiveHandle & handle,FILE * fp,std::string_view location,EntryFileStat & entry,std::string_view archiveFilename,panda_file::File::OpenMode openMode)174 std::unique_ptr<const panda_file::File> HandleArchive(ZipArchiveHandle &handle, FILE *fp, std::string_view location,
175                                                       EntryFileStat &entry, std::string_view archiveFilename,
176                                                       panda_file::File::OpenMode openMode)
177 {
178     std::unique_ptr<const panda_file::File> file;
179     // compressed or not 4 aligned, use anonymous memory
180     if (entry.IsCompressed() || (entry.GetOffset() & 0x3U) != 0) {
181         file = OpenPandaFileFromZipFile(handle, location, entry, archiveFilename);
182     } else {
183         LOG(INFO, PANDAFILE) << "Pandafile is uncompressed and 4 bytes aligned";
184         file = panda_file::File::OpenUncompressedArchive(fileno(fp), location, entry.GetUncompressedSize(),
185                                                          entry.GetOffset(), openMode);
186     }
187     return file;
188 }
189 
OpenZipPandaFile(FILE * fp,std::string_view location,std::string_view archiveFilename,panda_file::File::OpenMode openMode)190 std::unique_ptr<const panda_file::File> OpenZipPandaFile(FILE *fp, std::string_view location,
191                                                          std::string_view archiveFilename,
192                                                          panda_file::File::OpenMode openMode)
193 {
194     // Open Zipfile and do the extraction.
195     ZipArchiveHandle zipfile = nullptr;
196     auto openError = OpenArchiveFile(zipfile, fp);
197     if (openError != ZIPARCHIVE_OK) {
198         LOG(ERROR, PANDAFILE) << "Can't open archive " << location;
199         return nullptr;
200     }
201     bool tryDefault = archiveFilename.empty();
202     if (!tryDefault) {
203         if (LocateFile(zipfile, archiveFilename.data()) != ZIPARCHIVE_OK) {
204             LOG(INFO, PANDAFILE) << "Can't find entry with name '" << archiveFilename << "', will try "
205                                  << ARCHIVE_FILENAME;
206             tryDefault = true;
207         }
208     }
209     if (tryDefault) {
210         if (LocateFile(zipfile, ARCHIVE_FILENAME) != ZIPARCHIVE_OK) {
211             OpenPandaFileFromZipErrorHandler(zipfile);
212             LOG(ERROR, PANDAFILE) << "Can't find entry with " << ARCHIVE_FILENAME;
213             fclose(fp);
214             return nullptr;
215         }
216     }
217 
218     EntryFileStat entry = EntryFileStat();
219     if (GetCurrentFileInfo(zipfile, &entry) != ZIPARCHIVE_OK) {
220         OpenPandaFileFromZipErrorHandler(zipfile);
221         LOG(ERROR, PANDAFILE) << "GetCurrentFileInfo error";
222         return nullptr;
223     }
224     // check that file is not empty, otherwise crash at CloseArchiveFile
225     if (entry.GetUncompressedSize() == 0) {
226         OpenPandaFileFromZipErrorHandler(zipfile);
227         LOG(ERROR, PANDAFILE) << "Invalid panda file '" << (tryDefault ? ARCHIVE_FILENAME : archiveFilename) << "'";
228         return nullptr;
229     }
230     if (OpenCurrentFile(zipfile) != ZIPARCHIVE_OK) {
231         CloseCurrentFile(zipfile);
232         OpenPandaFileFromZipErrorHandler(zipfile);
233         LOG(ERROR, PANDAFILE) << "Can't OpenCurrentFile!";
234         return nullptr;
235     }
236     GetCurrentFileOffset(zipfile, &entry);
237     auto file = HandleArchive(zipfile, fp, location, entry, archiveFilename, openMode);
238     CloseCurrentFile(zipfile);
239     OpenPandaFileFromZipErrorHandler(zipfile);
240     return file;
241 }
242 
OpenPandaFile(std::string_view location,std::string_view archiveFilename,panda_file::File::OpenMode openMode)243 std::unique_ptr<const panda_file::File> OpenPandaFile(std::string_view location, std::string_view archiveFilename,
244                                                       panda_file::File::OpenMode openMode)
245 {
246     trace::ScopedTrace scopedTrace("Open panda file " + std::string(location));
247     uint32_t magic;
248 
249 #ifdef PANDA_TARGET_WINDOWS
250     constexpr char const *MODE = "rb";
251 #else
252     constexpr char const *MODE = "rbe";
253 #endif
254 
255     FILE *fp = fopen(std::string(location).c_str(), MODE);
256     if (fp == nullptr) {
257         LOG(ERROR, PANDAFILE) << "Can't fopen location: " << location;
258         return nullptr;
259     }
260     fseek(fp, 0, SEEK_SET);
261     if (fread(&magic, sizeof(magic), 1, fp) != 1) {
262         fclose(fp);
263         LOG(ERROR, PANDAFILE) << "Can't read from file!(magic) " << location;
264         return nullptr;
265     }
266     fseek(fp, 0, SEEK_SET);
267     std::unique_ptr<const panda_file::File> file;
268     if (IsZipMagic(magic)) {
269         file = OpenZipPandaFile(fp, location, archiveFilename, openMode);
270     } else {
271         file = panda_file::File::Open(location, openMode);
272     }
273     if (file != nullptr) {
274         fclose(fp);
275     }
276     return file;
277 }
278 
OpenPandaFileFromMemory(const void * buffer,size_t size,std::string tag)279 std::unique_ptr<const File> OpenPandaFileFromMemory(const void *buffer, size_t size, std::string tag)
280 {
281     size_t sizeToMmap = AlignUp(size, ark::os::mem::GetPageSize());
282     void *mem = os::mem::MapRWAnonymousRaw(sizeToMmap, false);
283     if (mem == nullptr) {
284         return nullptr;
285     }
286 
287     if (memcpy_s(mem, sizeToMmap, buffer, size) != 0) {
288         PLOG(ERROR, PANDAFILE) << "Failed to copy buffer into mem'";
289     }
290 
291     if (!tag.empty()) {
292         if (tag == "ArkTS Code") {
293             std::string memAddr = std::to_string(ToUintPtr(mem));
294             tag = tag + ":" + memAddr;
295         }
296         auto ret = os::mem::TagAnonymousMemory(mem, sizeToMmap, tag.c_str());
297         if (ret.has_value()) {
298             PLOG(ERROR, PANDAFILE) << "Can't tag mmap anonymous, errno: " << errno;
299         }
300     }
301 
302     os::mem::ConstBytePtr ptr(reinterpret_cast<std::byte *>(mem), sizeToMmap, os::mem::MmapDeleter);
303     if (ptr.Get() == nullptr) {
304         PLOG(ERROR, PANDAFILE) << "Failed to open panda file from memory'";
305         return nullptr;
306     }
307     std::hash<void *> hash;
308     return panda_file::File::OpenFromMemory(std::move(ptr), std::to_string(hash(mem)));
309 }
310 
OpenPandaFileFromSecureMemory(uint8_t * buffer,size_t size,std::string filename)311 std::unique_ptr<const File> OpenPandaFileFromSecureMemory(uint8_t *buffer, size_t size, std::string filename)
312 {
313     if (buffer == nullptr) {
314         PLOG(ERROR, PANDAFILE) << "OpenPandaFileFromSecureMemory buffer is nullptr'";
315         return nullptr;
316     }
317 
318     auto *mem = reinterpret_cast<std::byte *>(buffer);
319     os::mem::ConstBytePtr ptr(mem, size, [](std::byte *, size_t) noexcept {});
320     if (ptr.Get() == nullptr) {
321         PLOG(ERROR, PANDAFILE) << "Failed to open panda file from secure memory'";
322         return nullptr;
323     }
324 
325     std::hash<std::byte *> hash;
326     if (filename.empty()) {  // filename is sandbox path in application
327         filename = std::to_string(hash(mem));
328     }
329     return panda_file::File::OpenFromMemory(std::move(ptr), filename);
330 }
331 
332 class ClassIdxIterator {
333 public:
334     // NOLINTNEXTLINE(readability-identifier-naming)
335     using value_type = const uint8_t *;
336     // NOLINTNEXTLINE(readability-identifier-naming)
337     using difference_type = std::ptrdiff_t;
338     // NOLINTNEXTLINE(readability-identifier-naming)
339     using pointer = uint32_t *;
340     // NOLINTNEXTLINE(readability-identifier-naming)
341     using reference = uint32_t &;
342     // NOLINTNEXTLINE(readability-identifier-naming)
343     using iterator_category = std::random_access_iterator_tag;
344 
ClassIdxIterator(const File & file,const Span<const uint32_t> & span,size_t idx)345     ClassIdxIterator(const File &file, const Span<const uint32_t> &span, size_t idx)
346         : file_(file), span_(span), idx_(idx)
347     {
348     }
349 
350     ClassIdxIterator(const ClassIdxIterator &other) = default;
351     ClassIdxIterator(ClassIdxIterator &&other) = default;
352     ~ClassIdxIterator() = default;
353 
operator =(const ClassIdxIterator & other)354     ClassIdxIterator &operator=(const ClassIdxIterator &other)
355     {
356         if (&other != this) {
357             idx_ = other.idx_;
358         }
359 
360         return *this;
361     }
362 
operator =(ClassIdxIterator && other)363     ClassIdxIterator &operator=(ClassIdxIterator &&other) noexcept
364     {
365         idx_ = other.idx_;
366         return *this;
367     }
368 
operator +=(size_t n)369     ClassIdxIterator &operator+=(size_t n)
370     {
371         idx_ += n;
372         return *this;
373     }
374 
operator -=(size_t n)375     ClassIdxIterator &operator-=(size_t n)
376     {
377         idx_ -= n;
378         return *this;
379     }
380 
operator ++()381     ClassIdxIterator &operator++()
382     {
383         ++idx_;
384         return *this;
385     }
386 
operator --()387     ClassIdxIterator &operator--()
388     {
389         --idx_;
390         return *this;
391     }
392 
operator -(const ClassIdxIterator & other)393     difference_type operator-(const ClassIdxIterator &other)
394     {
395         return static_cast<difference_type>(idx_ - other.idx_);
396     }
397 
operator *() const398     value_type operator*() const
399     {
400         uint32_t id = span_[idx_];
401         return file_.GetStringData(File::EntityId(id)).data;
402     }
403 
IsValid() const404     bool IsValid() const
405     {
406         return idx_ < span_.Size();
407     }
408 
GetId() const409     uint32_t GetId() const
410     {
411         return span_[idx_];
412     }
413 
Begin(const File & file,const Span<const uint32_t> & span)414     static ClassIdxIterator Begin(const File &file, const Span<const uint32_t> &span)
415     {
416         return ClassIdxIterator(file, span, 0);
417     }
418 
End(const File & file,const Span<const uint32_t> & span)419     static ClassIdxIterator End(const File &file, const Span<const uint32_t> &span)
420     {
421         return ClassIdxIterator(file, span, span.Size());
422     }
423 
424 private:
425     const File &file_;
426     const Span<const uint32_t> &span_;
427     size_t idx_;
428 };
429 
File(std::string filename,os::mem::ConstBytePtr && base)430 File::File(std::string filename, os::mem::ConstBytePtr &&base)
431     : base_(std::forward<os::mem::ConstBytePtr>(base)),
432       filename_(std::move(filename)),
433       filenameHash_(CalcFilenameHash(filename_)),
434       fullFilename_(os::GetAbsolutePath(filename_).empty() ? filename_ : os::GetAbsolutePath(filename_)),
435       pandaCache_(std::make_unique<PandaCache>()),
436       uniqId_(MergeHashes(filenameHash_, GetHash32(reinterpret_cast<const uint8_t *>(GetHeader()), sizeof(Header))))
437 {
438 }
439 
~File()440 File::~File()
441 {
442     AnonMemSet::GetInstance().Remove(filename_);
443 }
444 
VersionToString(const std::array<uint8_t,File::VERSION_SIZE> & array)445 inline std::string VersionToString(const std::array<uint8_t, File::VERSION_SIZE> &array)
446 {
447     std::stringstream ss;
448 
449     for (size_t i = 0; i < File::VERSION_SIZE - 1; ++i) {
450         ss << static_cast<int>(array[i]);
451         ss << ".";
452     }
453     ss << static_cast<int>(array[File::VERSION_SIZE - 1]);
454 
455     return ss.str();
456 }
457 
458 // We can't use default std::array's comparision operators and need to implement
459 // own ones due to the bug in gcc: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95189
CompareVersions(const std::array<uint8_t,File::VERSION_SIZE> & lhs,const std::array<uint8_t,File::VERSION_SIZE> & rhs)460 inline int CompareVersions(const std::array<uint8_t, File::VERSION_SIZE> &lhs,
461                            const std::array<uint8_t, File::VERSION_SIZE> &rhs)
462 {
463     for (size_t i = 0; i < File::VERSION_SIZE; i++) {
464         if (lhs[i] == rhs[i]) {
465             continue;
466         }
467         return lhs[i] - rhs[i];
468     }
469     return 0;
470 }
471 
operator <(const std::array<uint8_t,File::VERSION_SIZE> & lhs,const std::array<uint8_t,File::VERSION_SIZE> & rhs)472 inline bool operator<(const std::array<uint8_t, File::VERSION_SIZE> &lhs,
473                       const std::array<uint8_t, File::VERSION_SIZE> &rhs)
474 {
475     return CompareVersions(lhs, rhs) < 0;
476 }
477 
operator >(const std::array<uint8_t,File::VERSION_SIZE> & lhs,const std::array<uint8_t,File::VERSION_SIZE> & rhs)478 inline bool operator>(const std::array<uint8_t, File::VERSION_SIZE> &lhs,
479                       const std::array<uint8_t, File::VERSION_SIZE> &rhs)
480 {
481     return CompareVersions(lhs, rhs) > 0;
482 }
483 
484 /* static */
Open(std::string_view filename,OpenMode openMode)485 std::unique_ptr<const File> File::Open(std::string_view filename, OpenMode openMode)
486 {
487     trace::ScopedTrace scopedTrace("Open panda file " + std::string(filename));
488     os::file::Mode mode = GetMode(openMode);
489     os::file::File file = os::file::Open(filename, mode);
490     if (!file.IsValid()) {
491         PLOG(ERROR, PANDAFILE) << "Failed to open panda file '" << filename << "'";
492         return nullptr;
493     }
494 
495     os::file::FileHolder fhHolder(file);
496 
497     auto res = file.GetFileSize();
498     if (!res) {
499         PLOG(ERROR, PANDAFILE) << "Failed to get size of panda file '" << filename << "'";
500         return nullptr;
501     }
502 
503     size_t size = res.Value();
504     if (size < sizeof(File::Header)) {
505         LOG(ERROR, PANDAFILE) << "Invalid panda file '" << filename << "' - has not header";
506         return nullptr;
507     }
508 
509     os::mem::ConstBytePtr ptr = os::mem::MapFile(file, GetProt(openMode), os::mem::MMAP_FLAG_PRIVATE, size).ToConst();
510     if (ptr.Get() == nullptr) {
511         PLOG(ERROR, PANDAFILE) << "Failed to map panda file '" << filename << "'";
512         return nullptr;
513     }
514 
515     if (!CheckHeader(ptr, filename, size)) {
516         return nullptr;
517     }
518 
519     // CC-OFFNXT(G.RES.09-CPP) ctor of class is private
520     return std::unique_ptr<File>(new File(filename.data(), std::move(ptr)));
521 }
522 
OpenUncompressedArchive(int fd,const std::string_view & filename,size_t size,uint32_t offset,OpenMode openMode)523 std::unique_ptr<const File> File::OpenUncompressedArchive(int fd, const std::string_view &filename, size_t size,
524                                                           uint32_t offset, OpenMode openMode)
525 {
526     trace::ScopedTrace scopedTrace("Open panda file " + std::string(filename));
527     auto file = os::file::File(fd);
528     if (!file.IsValid()) {
529         PLOG(ERROR, PANDAFILE) << "OpenUncompressedArchive: Failed to open panda file '" << filename << "'";
530         return nullptr;
531     }
532 
533     if (size < sizeof(File::Header)) {
534         LOG(ERROR, PANDAFILE) << "Invalid panda file size '" << filename << "'";
535         return nullptr;
536     }
537     LOG(DEBUG, PANDAFILE) << " size=" << size << " offset=" << offset << " " << filename;
538 
539     os::mem::ConstBytePtr ptr =
540         os::mem::MapFile(file, GetProt(openMode), os::mem::MMAP_FLAG_PRIVATE, size, offset).ToConst();
541     if (ptr.Get() == nullptr) {
542         PLOG(ERROR, PANDAFILE) << "Failed to map panda file '" << filename << "'";
543         return nullptr;
544     }
545     if (!CheckHeader(ptr, filename)) {
546         return nullptr;
547     }
548 
549     // CC-OFFNXT(G.RES.09-CPP) ctor of class is private
550     return std::unique_ptr<File>(new File(filename.data(), std::move(ptr)));
551 }
552 
ValidateChecksum(const os::mem::ConstBytePtr & ptr,const std::string_view & filename)553 bool ValidateChecksum(const os::mem::ConstBytePtr &ptr, const std::string_view &filename)
554 {
555     if (ptr.Get() == nullptr || ptr.GetSize() < sizeof(File::Header)) {
556         LOG(ERROR, PANDAFILE) << "Invalid panda file '" << filename << "'";
557         return false;
558     }
559     auto header = reinterpret_cast<const File::Header *>(ptr.Get());
560     uint32_t checksumSize = sizeof(File::Header::checksum);
561     uint32_t fileContentOffset = File::MAGIC_SIZE + checksumSize;
562     Span<const uint8_t> dataSpan(reinterpret_cast<const uint8_t *>(ptr.Get()), header->fileSize);
563     const uint8_t *pData = dataSpan.SubSpan(fileContentOffset).data();
564     uint32_t calChecksum = adler32(1, pData, header->fileSize - fileContentOffset);
565     if (header->checksum != calChecksum) {
566         LOG(ERROR, PANDAFILE) << "Checksum mismatch. The abc file has been corrupted. Expected checksum: 0x" << std::hex
567                               << header->checksum << ", Actual checksum: 0x" << std::hex << calChecksum << std::endl;
568         return false;
569     }
570     return true;
571 }
572 
CheckHeader(const os::mem::ConstBytePtr & ptr,const std::string_view & filename,const size_t & expectedLength)573 bool CheckHeader(const os::mem::ConstBytePtr &ptr, const std::string_view &filename, const size_t &expectedLength)
574 {
575     if (ptr.Get() == nullptr || ptr.GetSize() < sizeof(File::Header)) {
576         LOG(ERROR, PANDAFILE) << "Invalid panda file '" << filename << "'";
577         return false;
578     }
579     auto header = reinterpret_cast<const File::Header *>(ptr.Get());
580     if (expectedLength != 0 && expectedLength != header->fileSize) {
581         LOG(ERROR, PANDAFILE) << "File [" << filename << "]'s actual size [" << ptr.GetSize()
582                               << "] is not equal to Header's fileSize [" << header->fileSize << "]";
583         return false;
584     }
585     if (header->magic != File::MAGIC) {
586         LOG(ERROR, PANDAFILE) << "Invalid magic number '";
587         return false;
588     }
589 
590     if (!ValidateChecksum(ptr)) {
591         return false;
592     }
593 
594     auto fileVersion = header->version;
595 
596     if (fileVersion < MIN_VERSION || fileVersion > VERSION) {
597         LOG(ERROR, PANDAFILE) << "Unable to open file '" << filename << "' with bytecode version "
598                               << VersionToString(fileVersion);
599         if (fileVersion < MIN_VERSION) {
600             LOG(ERROR, PANDAFILE) << "Minimum supported version is " << VersionToString(MIN_VERSION);
601         } else {
602             LOG(ERROR, PANDAFILE) << "Maximum supported version is " << VersionToString(VERSION);
603         }
604         return false;
605     }
606     return true;
607 }
608 
609 /* static */
OpenFromMemory(os::mem::ConstBytePtr && ptr)610 std::unique_ptr<const File> File::OpenFromMemory(os::mem::ConstBytePtr &&ptr)
611 {
612     if (!CheckHeader(ptr, std::string_view())) {
613         return nullptr;
614     }
615 
616     // CC-OFFNXT(G.RES.09-CPP) ctor of class is private
617     return std::unique_ptr<File>(new File("", std::forward<os::mem::ConstBytePtr>(ptr)));
618 }
619 
620 /* static */
OpenFromMemory(os::mem::ConstBytePtr && ptr,std::string_view filename)621 std::unique_ptr<const File> File::OpenFromMemory(os::mem::ConstBytePtr &&ptr, std::string_view filename)
622 {
623     trace::ScopedTrace scopedTrace("Open panda file from RAM " + std::string(filename));
624 
625     if (!CheckHeader(ptr, filename)) {
626         return nullptr;
627     }
628 
629     // CC-OFFNXT(G.RES.09-CPP) ctor of class is private
630     return std::unique_ptr<File>(new File(filename.data(), std::forward<os::mem::ConstBytePtr>(ptr)));
631 }
632 
GetClassId(const uint8_t * mutf8Name) const633 File::EntityId File::GetClassId(const uint8_t *mutf8Name) const
634 {
635     auto classHashTable = GetClassHashTable();
636     if (!classHashTable.empty()) {
637         return GetClassIdFromClassHashTable(mutf8Name);
638     }
639 
640     auto classIdx = GetClasses();
641 
642     auto it = std::lower_bound(ClassIdxIterator::Begin(*this, classIdx), ClassIdxIterator::End(*this, classIdx),
643                                mutf8Name, utf::Mutf8Less());
644     if (!it.IsValid()) {
645         return EntityId();
646     }
647 
648     if (utf::CompareMUtf8ToMUtf8(mutf8Name, *it) == 0) {
649         return EntityId(it.GetId());
650     }
651 
652     return EntityId();
653 }
654 
CalcFilenameHash(const std::string & filename)655 uint32_t File::CalcFilenameHash(const std::string &filename)
656 {
657     return GetHash32String(reinterpret_cast<const uint8_t *>(filename.c_str()));
658 }
659 
GetLiteralArraysId() const660 File::EntityId File::GetLiteralArraysId() const
661 {
662     const Header *header = GetHeader();
663     return EntityId(header->literalarrayIdxOff);
664 }
665 
GetClassIdFromClassHashTable(const uint8_t * mutf8Name) const666 File::EntityId File::GetClassIdFromClassHashTable(const uint8_t *mutf8Name) const
667 {
668     auto classHashTable = GetClassHashTable();
669     auto hash = GetHash32String(mutf8Name);
670     auto pos = hash & (classHashTable.size() - 1);
671     auto entityPair = &classHashTable[pos];
672 
673     if (entityPair->descriptorHash % classHashTable.size() != pos) {
674         return File::EntityId();
675     }
676 
677     while (true) {
678         if (hash == entityPair->descriptorHash) {
679             auto entityId = File::EntityId(entityPair->entityIdOffset);
680             auto descriptor = GetStringData(entityId).data;
681             if (entityId.IsValid() && utf::CompareMUtf8ToMUtf8(descriptor, mutf8Name) == 0) {
682                 return entityId;
683             }
684         }
685         if (entityPair->nextPos == 0) {
686             break;
687         }
688         entityPair = &classHashTable[entityPair->nextPos - 1];
689     }
690 
691     return File::EntityId();
692 }
693 
694 }  // namespace ark::panda_file
695