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