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 <algorithm>
17#include <cerrno>
18#include <cstdio>
19#include <cstring>
20#include <map>
21#include <memory>
22#include <string>
23#include <variant>
24
25#include "data_protect.h"
26#include "file_format_version.h"
27#include "file-inl.h"
28#include "file_items.h"
29#include "mem/mem.h"
30#include "os/file.h"
31#include "os/filesystem.h"
32#include "os/mem.h"
33#include "panda_cache.h"
34#include "securec.h"
35#include "trace/trace.h"
36#include "utils/hash.h"
37#include "utils/logger.h"
38#include "utils/utf.h"
39#include "utils/span.h"
40#include "zip_archive.h"
41#include "zlib.h"
42
43namespace panda::panda_file {
44// NOLINTNEXTLINE(readability-identifier-naming, modernize-avoid-c-arrays)
45const char *ARCHIVE_FILENAME = "classes.abc";
46// NOLINTNEXTLINE(readability-identifier-naming, modernize-avoid-c-arrays)
47const char *ARCHIVE_SPLIT = "!/";
48
49const std::array<uint8_t, File::MAGIC_SIZE> File::MAGIC {'P', 'A', 'N', 'D', 'A', '\0', '\0', '\0'};
50
51// Name anonymous maps for perfing tools finding symbol file correctly.
52// NOLINTNEXTLINE(readability-identifier-naming, modernize-avoid-c-arrays)
53const char *ANONMAPNAME_PERFIX = "panda-";
54
55os::file::Mode GetMode(panda_file::File::OpenMode open_mode)
56{
57    switch (open_mode) {
58        case File::READ_ONLY: {
59            return os::file::Mode::READONLY;
60        }
61        case File::READ_WRITE: {
62#ifdef PANDA_TARGET_WINDOWS
63            return os::file::Mode::READWRITE;
64#else
65            return os::file::Mode::READONLY;
66#endif
67        }
68        case File::WRITE_ONLY: {
69            return os::file::Mode::WRITEONLY;
70        }
71        default: {
72            break;
73        }
74    }
75
76    UNREACHABLE();
77}
78
79static uint32_t GetProt(panda_file::File::OpenMode mode)
80{
81    uint32_t prot = os::mem::MMAP_PROT_READ;
82    if (mode == File::READ_WRITE) {
83        prot |= os::mem::MMAP_PROT_WRITE;
84    }
85    return prot;
86}
87
88class AnonMemSet {
89public:
90    using MemNameSet = std::map<std::string, std::string>;
91    using InsertResult = std::map<std::string, std::string>::iterator;
92
93    static AnonMemSet &GetInstance()
94    {
95        static AnonMemSet anon_mem_set;
96        return anon_mem_set;
97    }
98
99    InsertResult Insert(const std::string &file_name, const std::string &anon_mem_name)
100    {
101        return mem_name_set_.emplace(file_name, anon_mem_name).first;
102    }
103
104    void Remove(const std::string &file_name)
105    {
106        auto it = mem_name_set_.find(file_name);
107        if (it != mem_name_set_.end()) {
108            mem_name_set_.erase(it);
109        }
110    }
111
112private:
113    MemNameSet mem_name_set_;
114};
115
116std::unique_ptr<const File> OpenPandaFileOrZip(std::string_view location, panda_file::File::OpenMode open_mode)
117{
118    std::string_view archive_filename = ARCHIVE_FILENAME;
119    std::size_t archive_split_index = location.find(ARCHIVE_SPLIT);
120    if (archive_split_index != std::string::npos) {
121        archive_filename = location.substr(archive_split_index + 2);  // 2 - archive split size
122        location = location.substr(0, archive_split_index);
123    }
124
125    return OpenPandaFile(location, archive_filename, open_mode);
126}
127
128// NOLINTNEXTLINE(google-runtime-references)
129void OpenPandaFileFromZipErrorHandler(ZipArchiveHandle &handle)
130{
131    if (handle != nullptr) {
132        if (panda::CloseArchiveFile(handle) != ZIPARCHIVE_OK) {
133            LOG(ERROR, PANDAFILE) << "CloseArchiveFile failed!";
134        }
135    }
136}
137
138std::unique_ptr<const panda_file::File> OpenPandaFileFromZipFile(ZipArchiveHandle &handle, std::string_view location,
139                                                                 EntryFileStat &entry,
140                                                                 const std::string_view &archive_name)
141{
142    uint32_t uncompressed_length = entry.GetUncompressedSize();
143    ASSERT(uncompressed_length != 0U);
144
145    size_t size_to_mmap = AlignUp(uncompressed_length, panda::os::mem::GetPageSize());
146    void *mem = os::mem::MapRWAnonymousRaw(size_to_mmap, false);
147    if (mem == nullptr) {
148        LOG(ERROR, PANDAFILE) << "Can't mmap anonymous!";
149        return nullptr;
150    }
151    os::mem::BytePtr ptr(reinterpret_cast<std::byte *>(mem), size_to_mmap, os::mem::MmapDeleter);
152    std::stringstream ss;
153    ss << ANONMAPNAME_PERFIX << archive_name << " extracted in memory from " << location;
154    auto it = AnonMemSet::GetInstance().Insert(std::string(location), ss.str());
155    auto ret = os::mem::TagAnonymousMemory(reinterpret_cast<void *>(ptr.Get()), size_to_mmap, it->second.c_str());
156    if (ret.has_value()) {
157        LOG(ERROR, PANDAFILE) << "Can't tag mmap anonymous!";
158        return nullptr;
159    }
160
161    auto extract_error = ExtractToMemory(handle, reinterpret_cast<uint8_t *>(ptr.Get()), size_to_mmap);
162    if (extract_error != 0) {
163        LOG(ERROR, PANDAFILE) << "Can't extract!";
164        return nullptr;
165    }
166
167    os::mem::ConstBytePtr ConstPtr = ptr.ToConst();
168    return panda_file::File::OpenFromMemory(std::move(ConstPtr), location);
169}
170
171// NOLINTNEXTLINE(google-runtime-references)
172std::unique_ptr<const panda_file::File> HandleArchive(ZipArchiveHandle &handle, FILE *fp, std::string_view location,
173                                                      EntryFileStat &entry, const std::string_view &archive_filename,
174                                                      panda_file::File::OpenMode open_mode)
175{
176    std::unique_ptr<const panda_file::File> file;
177    // compressed or not 4 aligned, use anonymous memory
178    if (entry.IsCompressed() || (entry.GetOffset() & 0x3U) != 0) {
179        file = OpenPandaFileFromZipFile(handle, location, entry, archive_filename);
180    } else {
181        LOG(INFO, PANDAFILE) << "Pandafile is uncompressed and 4 bytes aligned";
182        file = panda_file::File::OpenUncompressedArchive(fileno(fp), location, entry.GetUncompressedSize(),
183                                                         entry.GetOffset(), open_mode);
184    }
185    return file;
186}
187
188std::unique_ptr<const panda_file::File> OpenPandaFileFromZip(FILE *fp, std::string_view location,
189                                                             std::string_view archive_filename,
190                                                             panda_file::File::OpenMode open_mode)
191{
192    // Open Zipfile and do the extraction.
193    ZipArchiveHandle zipfile = nullptr;
194    if (OpenArchiveFile(zipfile, fp) != ZIPARCHIVE_OK) {
195        LOG(ERROR, PANDAFILE) << "Can't open archive " << location;
196        return nullptr;
197    }
198    bool try_default = archive_filename.empty();
199    if (!try_default && LocateFile(zipfile, archive_filename.data()) != ZIPARCHIVE_OK) {
200        LOG(INFO, PANDAFILE) << "Can't find entry with name '" <<
201            archive_filename << "', will try " << ARCHIVE_FILENAME;
202        try_default = true;
203    }
204    if (try_default && LocateFile(zipfile, ARCHIVE_FILENAME) != ZIPARCHIVE_OK) {
205        OpenPandaFileFromZipErrorHandler(zipfile);
206        LOG(ERROR, PANDAFILE) << "Can't find entry with " << ARCHIVE_FILENAME;
207        return nullptr;
208    }
209
210    EntryFileStat entry = EntryFileStat();
211    if (GetCurrentFileInfo(zipfile, &entry) != ZIPARCHIVE_OK) {
212        OpenPandaFileFromZipErrorHandler(zipfile);
213        LOG(ERROR, PANDAFILE) << "GetCurrentFileInfo error";
214        return nullptr;
215    }
216    // check that file is not empty, otherwise crash at CloseArchiveFile
217    if (entry.GetUncompressedSize() == 0) {
218        OpenPandaFileFromZipErrorHandler(zipfile);
219        LOG(ERROR, PANDAFILE) << "Invalid panda file '" << (try_default ? ARCHIVE_FILENAME : archive_filename) << "'";
220        return nullptr;
221    }
222    if (OpenCurrentFile(zipfile) != ZIPARCHIVE_OK) {
223        CloseCurrentFile(zipfile);
224        OpenPandaFileFromZipErrorHandler(zipfile);
225        LOG(ERROR, PANDAFILE) << "Can't OpenCurrentFile!";
226        return nullptr;
227    }
228    GetCurrentFileOffset(zipfile, &entry);
229    std::unique_ptr<const panda_file::File> file = HandleArchive(zipfile, fp, location, entry,
230                                                                 archive_filename, open_mode);
231    CloseCurrentFile(zipfile);
232    OpenPandaFileFromZipErrorHandler(zipfile);
233    return file;
234}
235
236std::unique_ptr<const panda_file::File> OpenPandaFile(std::string_view location, std::string_view archive_filename,
237                                                      panda_file::File::OpenMode open_mode)
238{
239    trace::ScopedTrace scoped_trace("Open panda file " + std::string(location));
240    uint32_t magic;
241
242#ifdef PANDA_TARGET_WINDOWS
243    constexpr char const *mode = "rb";
244#else
245    constexpr char const *mode = "rbe";
246#endif
247
248    FILE *fp = fopen(std::string(location).c_str(), mode);
249    if (fp == nullptr) {
250        LOG(ERROR, PANDAFILE) << "Can't fopen location: " << location;
251        return nullptr;
252    }
253    (void)fseek(fp, 0, SEEK_SET);
254    if (fread(&magic, sizeof(magic), 1, fp) != 1) {
255        fclose(fp);
256        LOG(ERROR, PANDAFILE) << "Can't read from file!(magic) " << location;
257        return nullptr;
258    }
259    (void)fseek(fp, 0, SEEK_SET);
260    std::unique_ptr<const panda_file::File> file;
261    if (IsZipMagic(magic)) {
262        file = OpenPandaFileFromZip(fp, location, archive_filename, open_mode);
263    } else {
264        file = panda_file::File::Open(location, open_mode);
265    }
266    fclose(fp);
267    return file;
268}
269
270std::unique_ptr<const File> OpenPandaFileFromMemory(const void *buffer, size_t size, std::string tag)
271{
272    size_t size_to_mmap = AlignUp(size, panda::os::mem::GetPageSize());
273    void *mem = os::mem::MapRWAnonymousRaw(size_to_mmap, false);
274    if (mem == nullptr) {
275        return nullptr;
276    }
277
278    if (memcpy_s(mem, size_to_mmap, buffer, size) != 0) {
279        PLOG(ERROR, PANDAFILE) << "Failed to copy buffer into mem'";
280    }
281
282    if (!tag.empty()) {
283        if (tag == "ArkTS Code") {
284            std::string memAddr = std::to_string(ToUintPtr(mem));
285            tag = tag + ":" + memAddr;
286        }
287        auto ret = os::mem::TagAnonymousMemory(mem, size_to_mmap, tag.c_str());
288        if (ret.has_value()) {
289            PLOG(DEBUG, PANDAFILE) << "Can't tag mmap anonymous, errno: " << errno;
290        }
291    }
292
293    os::mem::ConstBytePtr ptr(reinterpret_cast<std::byte *>(mem), size_to_mmap, os::mem::MmapDeleter);
294    if (ptr.Get() == nullptr) {
295        PLOG(ERROR, PANDAFILE) << "Failed to open panda file from memory'";
296        return nullptr;
297    }
298    std::hash<void *> hash;
299    return panda_file::File::OpenFromMemory(std::move(ptr), std::to_string(hash(mem)));
300}
301
302std::unique_ptr<const File> OpenPandaFileFromSecureMemory(uint8_t *buffer, size_t size)
303{
304    if (buffer == nullptr) {
305        PLOG(ERROR, PANDAFILE) << "OpenPandaFileFromSecureMemory buffer is nullptr'";
306        return nullptr;
307    }
308
309    if (!CheckSecureMem(reinterpret_cast<uintptr_t>(buffer), size)) {
310        PLOG(ERROR, PANDAFILE) << "Secure memory check failed, please execute in secure memory.";
311        return nullptr;
312    }
313
314    std::byte *mem = reinterpret_cast<std::byte *>(buffer);
315    os::mem::ConstBytePtr ptr(mem, size, nullptr);
316    if (ptr.Get() == nullptr) {
317        PLOG(ERROR, PANDAFILE) << "Failed to open panda file from secure memory'";
318        return nullptr;
319    }
320
321    std::hash<std::byte *> hash;
322    return panda_file::File::OpenFromMemory(std::move(ptr), std::to_string(hash(mem)));
323}
324
325inline bool CheckSecureMem(uintptr_t mem, size_t size)
326{
327    static bool has_open = false;
328    static DataProtect start = DataProtect();
329    static DataProtect end = DataProtect();
330    uintptr_t secure_mem_start;
331    uintptr_t secure_mem_end;
332    if (!has_open) {
333        int fd = open(PROC_SELF_XPM_REGION_PATH, O_RDONLY);
334        if (fd < 0) {
335            LOG(ERROR, PANDAFILE) << "Can not open xpm proc file, do not check secure memory anymore.";
336            // No verification is performed when a file fails to be opened.
337            has_open = true;
338            return true;
339        }
340        char xpm_validate_region[XPM_PROC_LENGTH] = {0};
341        int ret = read(fd, xpm_validate_region, sizeof(xpm_validate_region));
342        if (ret <= 0) {
343            LOG(ERROR, PANDAFILE) << "Read xpm proc file failed";
344            close(fd);
345            return false;
346        }
347        close(fd);
348        if (sscanf_s(xpm_validate_region, "%lx-%lx", &secure_mem_start, &secure_mem_end) <= 0) {
349            LOG(ERROR, PANDAFILE) << "sscanf_s xpm validate region failed";
350            return false;
351        }
352        // The check is not performed when the file is already opened.
353        has_open = true;
354        LOG(DEBUG, PANDAFILE) << "Successfully open xpm region.";
355        start.Update(secure_mem_start);
356        end.Update(secure_mem_end);
357    }
358    secure_mem_start = start.GetOriginPointer();
359    secure_mem_end = end.GetOriginPointer();
360    // xpm proc does not exist, the read value is 0, and the check is not performed.
361    if (secure_mem_start == 0 && secure_mem_end == 0) {
362        LOG(ERROR, PANDAFILE) << "Secure memory check: xpm proc does not exist, do not check secure memory anymore.";
363        return true;
364    }
365    if (mem < secure_mem_start || (size > (std::numeric_limits<uintptr_t>::max() - mem)) ||
366        (mem + size) > secure_mem_end) {
367        LOG(ERROR, PANDAFILE) << "Secure memory check failed, mem out of secure memory region.";
368        return false;
369    }
370    return true;
371}
372
373class ClassIdxIterator {
374public:
375    using value_type = const uint8_t *;
376    using difference_type = std::ptrdiff_t;
377    using pointer = uint32_t *;
378    using reference = uint32_t &;
379    using iterator_category = std::random_access_iterator_tag;
380
381    ClassIdxIterator(const File &file, const Span<const uint32_t> &span, size_t idx)
382        : file_(file), span_(span), idx_(idx)
383    {
384    }
385
386    ClassIdxIterator(const ClassIdxIterator &other) = default;
387    ClassIdxIterator(ClassIdxIterator &&other) = default;
388    ~ClassIdxIterator() = default;
389
390    ClassIdxIterator &operator=(const ClassIdxIterator &other)
391    {
392        if (&other != this) {
393            idx_ = other.idx_;
394        }
395
396        return *this;
397    }
398
399    ClassIdxIterator &operator=(ClassIdxIterator &&other) noexcept
400    {
401        idx_ = other.idx_;
402        return *this;
403    }
404
405    ClassIdxIterator &operator+=(size_t n)
406    {
407        idx_ += n;
408        return *this;
409    }
410
411    ClassIdxIterator &operator-=(size_t n)
412    {
413        idx_ -= n;
414        return *this;
415    }
416
417    ClassIdxIterator &operator++()
418    {
419        ++idx_;
420        return *this;
421    }
422
423    ClassIdxIterator &operator--()
424    {
425        --idx_;
426        return *this;
427    }
428
429    difference_type operator-(const ClassIdxIterator &other)
430    {
431        return idx_ - other.idx_;
432    }
433
434    value_type operator*() const
435    {
436        uint32_t id = span_[idx_];
437        return file_.GetStringData(File::EntityId(id)).data;
438    }
439
440    bool IsValid() const
441    {
442        return idx_ < span_.Size();
443    }
444
445    uint32_t GetId() const
446    {
447        return span_[idx_];
448    }
449
450    static ClassIdxIterator Begin(const File &file, const Span<const uint32_t> &span)
451    {
452        return ClassIdxIterator(file, span, 0);
453    }
454
455    static ClassIdxIterator End(const File &file, const Span<const uint32_t> &span)
456    {
457        return ClassIdxIterator(file, span, span.Size());
458    }
459
460private:
461    const File &file_;
462    const Span<const uint32_t> &span_;
463    size_t idx_;
464};
465
466File::File(std::string filename, os::mem::ConstBytePtr &&base)
467    : base_(std::forward<os::mem::ConstBytePtr>(base)),
468      FILENAME(std::move(filename)),
469      FILENAME_HASH(CalcFilenameHash(FILENAME)),
470#ifdef ENABLE_FULL_FILE_FIELDS
471      FULL_FILENAME(os::GetAbsolutePath(FILENAME)),
472      panda_cache_(std::make_unique<PandaCache>()),
473#endif
474      UNIQ_ID(merge_hashes(FILENAME_HASH, GetHash32(reinterpret_cast<const uint8_t *>(GetHeader()), sizeof(Header))))
475{
476}
477
478File::~File()
479{
480    AnonMemSet::GetInstance().Remove(FILENAME);
481}
482
483inline std::string VersionToString(const std::array<uint8_t, File::VERSION_SIZE> &array)
484{
485    std::stringstream ss;
486
487    for (size_t i = 0; i < File::VERSION_SIZE - 1; ++i) {
488        ss << static_cast<int>(array[i]);
489        ss << ".";
490    }
491    ss << static_cast<int>(array[File::VERSION_SIZE - 1]);
492
493    return ss.str();
494}
495
496// We can't use default std::array's comparision operators and need to implement
497// own ones due to the bug in gcc: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95189
498inline int CompareVersions(const std::array<uint8_t, File::VERSION_SIZE> &lhs,
499                           const std::array<uint8_t, File::VERSION_SIZE> &rhs)
500{
501    for (size_t i = 0; i < File::VERSION_SIZE; i++) {
502        if (lhs[i] == rhs[i]) {
503            continue;
504        }
505        return lhs[i] - rhs[i];
506    }
507    return 0;
508}
509
510inline bool operator<(const std::array<uint8_t, File::VERSION_SIZE> &lhs,
511                      const std::array<uint8_t, File::VERSION_SIZE> &rhs)
512{
513    return CompareVersions(lhs, rhs) < 0;
514}
515
516inline bool operator>(const std::array<uint8_t, File::VERSION_SIZE> &lhs,
517                      const std::array<uint8_t, File::VERSION_SIZE> &rhs)
518{
519    return CompareVersions(lhs, rhs) > 0;
520}
521
522/* static */
523std::unique_ptr<const File> File::Open(std::string_view filename, OpenMode open_mode)
524{
525    trace::ScopedTrace scoped_trace("Open panda file " + std::string(filename));
526    os::file::Mode mode = GetMode(open_mode);
527    os::file::File file = os::file::Open(filename, mode);
528    if (!file.IsValid()) {
529        PLOG(ERROR, PANDAFILE) << "Failed to open panda file '" << filename << "'";
530        return nullptr;
531    }
532
533    os::file::FileHolder fh_holder(file);
534
535    auto res = file.GetFileSize();
536    if (!res) {
537        PLOG(ERROR, PANDAFILE) << "Failed to get size of panda file '" << filename << "'";
538        return nullptr;
539    }
540
541    size_t size = res.Value();
542    if (size < sizeof(File::Header)) {
543        LOG(ERROR, PANDAFILE) << "Invalid panda file '" << filename << "' - missing or incomplete header" <<
544                                 ". Abc file is corrupted";
545        return nullptr;
546    }
547
548    os::mem::ConstBytePtr ptr = os::mem::MapFile(file, GetProt(open_mode), os::mem::MMAP_FLAG_PRIVATE, size).ToConst();
549    if (ptr.Get() == nullptr) {
550        PLOG(ERROR, PANDAFILE) << "Failed to map panda file '" << filename << "'";
551        return nullptr;
552    }
553
554    if (!CheckHeader(ptr, filename)) {
555        return nullptr;
556    }
557
558    return std::unique_ptr<File>(new File(filename.data(), std::move(ptr)));
559}
560
561std::unique_ptr<const File> File::OpenUncompressedArchive(int fd, const std::string_view &filename, size_t size,
562                                                          uint32_t offset, OpenMode open_mode)
563{
564    trace::ScopedTrace scoped_trace("Open panda file " + std::string(filename));
565    auto file = os::file::File(fd);
566    if (!file.IsValid()) {
567        PLOG(ERROR, PANDAFILE) << "OpenUncompressedArchive: Failed to open panda file '" << filename << "'";
568        return nullptr;
569    }
570
571    if (size < sizeof(File::Header)) {
572        LOG(ERROR, PANDAFILE) << "Invalid panda file size '" << filename << "'" << ". Abc file is corrupted";
573        return nullptr;
574    }
575    LOG(DEBUG, PANDAFILE) << " size=" << size << " offset=" << offset << " " << filename;
576
577    os::mem::ConstBytePtr ptr =
578        os::mem::MapFile(file, GetProt(open_mode), os::mem::MMAP_FLAG_PRIVATE, size, offset).ToConst();
579    if (ptr.Get() == nullptr) {
580        PLOG(ERROR, PANDAFILE) << "Failed to map panda file '" << filename << "'";
581        return nullptr;
582    }
583    if (!CheckHeader(ptr, filename)) {
584        return nullptr;
585    }
586
587    return std::unique_ptr<File>(new File(filename.data(), std::move(ptr)));
588}
589
590template <typename T = uint32_t>
591bool CheckHeaderElementOffset(size_t offset, size_t number, size_t file_size)
592{
593    auto number_size = number * sizeof(T);
594    if (offset > file_size || number_size > file_size || offset > file_size - number_size) {
595        return false;
596    }
597    return true;
598}
599
600bool CheckHeader(const os::mem::ConstBytePtr &ptr, const std::string_view &filename)
601{
602    if (ptr.Get() == nullptr || ptr.GetSize() < sizeof(File::Header)) {
603        LOG(ERROR, PANDAFILE) << "Invalid panda file '" << filename << "'" << ". Abc file is corrupted";
604        return false;
605    }
606    auto header = reinterpret_cast<const File::Header *>(reinterpret_cast<uintptr_t>(ptr.Get()));
607    if (header->magic != File::MAGIC) {
608        LOG(ERROR, PANDAFILE) << "Invalid magic number" << ". Abc file is corrupted";
609        return false;
610    }
611
612    CheckFileVersion(header->version, filename);
613
614    if (header->file_size < sizeof(File::Header) || header->file_size > ptr.GetSize()) {
615        LOG(ERROR, PANDAFILE) << "Invalid panda file size " << header->file_size << ". Abc file is corrupted";
616        return false;
617    }
618
619    if (!CheckHeaderElementOffset<uint8_t>(header->foreign_off, header->foreign_size, header->file_size)) {
620        LOG(ERROR, PANDAFILE) << "Invalid panda file foreign_off " << header->foreign_off <<
621            " or foreign_size " << header->foreign_size << ". Abc file is corrupted";
622        return false;
623    }
624
625    if (!CheckHeaderElementOffset(header->class_idx_off, header->num_classes, header->file_size)) {
626        LOG(ERROR, PANDAFILE) << "Invalid panda file class_idx_off " << header->class_idx_off <<
627            " or num_classes " << header->num_classes << ". Abc file is corrupted";
628        return false;
629    }
630
631    if (!CheckHeaderElementOffset(header->lnp_idx_off, header->num_lnps, header->file_size)) {
632        LOG(ERROR, PANDAFILE) << "Invalid panda file lnp_idx_off " << header->lnp_idx_off <<
633            " or num_lnps " << header->num_lnps << ". Abc file is corrupted";
634        return false;
635    }
636
637    if (ContainsLiteralArrayInHeader(header->version)) {
638        if (!CheckHeaderElementOffset(header->literalarray_idx_off, header->num_literalarrays, header->file_size)) {
639            LOG(ERROR, PANDAFILE) << "Invalid panda file literalarray_idx_off " << header->literalarray_idx_off <<
640                                     " or num_literalarrays " << header->num_literalarrays <<
641                                     ". Abc file is corrupted";
642            return false;
643        }
644    } else {
645        if (header->literalarray_idx_off != INVALID_INDEX || header->num_literalarrays != INVALID_OFFSET) {
646            LOG(ERROR, PANDAFILE) << "Invalid panda file literalarray_idx_off " << header->literalarray_idx_off <<
647                                     " or num_literalarrays " << header->num_literalarrays <<
648                                     ", The literalarray_idx_off and num_literalarrays should be reserved." <<
649                                     " Abc file is corrupted";
650            return false;
651        }
652    }
653
654    if (!CheckHeaderElementOffset<File::IndexHeader>(header->index_section_off, header->num_indexes,
655        header->file_size)) {
656        LOG(ERROR, PANDAFILE) << "Invalid panda file index_section_off " << header->index_section_off <<
657            " or num_indexes " << header->num_indexes << ". Abc file is corrupted";
658        return false;
659    }
660
661    return true;
662}
663
664void CheckFileVersion(const std::array<uint8_t, File::VERSION_SIZE> &file_version, const std::string_view &filename)
665{
666#ifdef ERROR_AS_FATAL
667#define LOG_LEVEL FATAL
668#else
669#define LOG_LEVEL ERROR
670#endif
671    if (file_version == version) {
672        return;
673    }
674    if (file_version < minVersion) {
675        LOG(LOG_LEVEL, PANDAFILE) << "Unable to open file '" << filename << "' with abc file version "
676            << VersionToString(file_version)
677            << ". Minimum supported abc file version on the current system image is " << VersionToString(minVersion)
678            << ". Please upgrade the sdk tools to generate supported version of abc files "
679            << "or execute the abc file on former version of system image";
680    } else if (file_version > version) {
681        LOG(LOG_LEVEL, PANDAFILE) << "Unable to open file '" << filename << "' with abc file version "
682            << VersionToString(file_version)
683            << ". Maximum supported abc file version on the current system image is " << VersionToString(version)
684            << ". Please upgrade the system image or use former version of SDK tools to generate abc files";
685    } else if (incompatibleVersion.count(file_version) != 0) {
686        LOG(LOG_LEVEL, PANDAFILE) << "Unable to open file '" << filename << "' with  abc file version "
687            << VersionToString(file_version) << ". Current system image version is "
688            << VersionToString(version) << ", while abc file version is " << VersionToString(file_version)
689            << ". The version "<< VersionToString(file_version)
690            << " is not a compatible version, can't run on system image of version " << VersionToString(version)
691            << ". Please use sdk tools and system image in pairs "
692            << "and make the version of sdk tools and system image consistent";
693    }
694#undef LOG_LEVEL
695}
696/* static */
697std::unique_ptr<const File> File::OpenFromMemory(os::mem::ConstBytePtr &&ptr)
698{
699    if (!CheckHeader(ptr, std::string_view())) {
700        return nullptr;
701    }
702
703    return std::unique_ptr<File>(new File("", std::forward<os::mem::ConstBytePtr>(ptr)));
704}
705
706/* static */
707std::unique_ptr<const File> File::OpenFromMemory(os::mem::ConstBytePtr &&ptr, std::string_view filename)
708{
709    trace::ScopedTrace scoped_trace("Open panda file from RAM " + std::string(filename));
710
711    if (!CheckHeader(ptr, filename)) {
712        return nullptr;
713    }
714
715    return std::unique_ptr<File>(new File(filename.data(), std::forward<os::mem::ConstBytePtr>(ptr)));
716}
717
718File::EntityId File::GetClassId(const uint8_t *mutf8_name) const
719{
720    auto class_hash_table = GetClassHashTable();
721    if (!class_hash_table.empty()) {
722        return GetClassIdFromClassHashTable(mutf8_name);
723    }
724
725    auto class_idx = GetClasses();
726
727    auto it = std::lower_bound(ClassIdxIterator::Begin(*this, class_idx), ClassIdxIterator::End(*this, class_idx),
728                               mutf8_name, utf::Mutf8Less());
729    if (!it.IsValid()) {
730        return EntityId();
731    }
732
733    if (utf::CompareMUtf8ToMUtf8(mutf8_name, *it) == 0) {
734        return EntityId(it.GetId());
735    }
736
737    return EntityId();
738}
739
740uint32_t File::CalcFilenameHash(const std::string &filename)
741{
742    return GetHash32String(reinterpret_cast<const uint8_t *>(filename.c_str()));
743}
744
745File::EntityId File::GetLiteralArraysId() const
746{
747    const Header *header = GetHeader();
748    return EntityId(header->literalarray_idx_off);
749}
750
751File::EntityId File::GetClassIdFromClassHashTable(const uint8_t *mutf8_name) const
752{
753    auto class_hash_table = GetClassHashTable();
754    auto hash = GetHash32String(mutf8_name);
755    auto pos = hash & (class_hash_table.size() - 1);
756    auto entity_pair = &class_hash_table[pos];
757
758    if (entity_pair->descriptor_hash % class_hash_table.size() != pos) {
759        return File::EntityId();
760    }
761
762    while (true) {
763        if (hash == entity_pair->descriptor_hash) {
764            auto entity_id = File::EntityId(entity_pair->entity_id_offset);
765            auto descriptor = GetStringData(entity_id).data;
766            if (entity_id.IsValid() && utf::CompareMUtf8ToMUtf8(descriptor, mutf8_name) == 0) {
767                return entity_id;
768            }
769        }
770        if (entity_pair->next_pos == 0) {
771            break;
772        }
773        entity_pair = &class_hash_table[entity_pair->next_pos - 1];
774    }
775
776    return File::EntityId();
777}
778
779
780bool ContainsLiteralArrayInHeader(const std::array<uint8_t, File::VERSION_SIZE> &version)
781{
782    return panda::panda_file::IsVersionLessOrEqual(version, LAST_CONTAINS_LITERAL_IN_HEADER_VERSION);
783}
784
785bool File::ValidateChecksum(uint32_t *cal_checksum_out) const
786{
787    constexpr uint32_t CHECKSUM_SIZE = 4U;
788    // The checksum calculation does not include magic or checksum, so the offset needs to be added
789    constexpr uint32_t FILE_CONTENT_OFFSET = File::MAGIC_SIZE + CHECKSUM_SIZE;
790    uint32_t file_size = GetHeader()->file_size;
791    uint32_t cal_checksum = adler32(1, GetBase() + FILE_CONTENT_OFFSET, file_size - FILE_CONTENT_OFFSET);
792
793    if (cal_checksum_out != nullptr) {
794        *cal_checksum_out = cal_checksum;
795    }
796
797    return GetHeader()->checksum == cal_checksum;
798}
799
800void File::ThrowIfWithCheck(bool cond, const std::string_view& msg, const std::string_view& tag) const
801{
802    if (UNLIKELY(cond)) {
803        uint32_t cal_checksum = 0;
804        bool is_checksum_match = ValidateChecksum(&cal_checksum);
805        if (!is_checksum_match) {
806            LOG(FATAL, PANDAFILE) << msg << ", checksum mismatch. The abc file has been corrupted. "
807                                         << "Expected checksum: 0x" << std::hex << GetHeader()->checksum
808                                         << ", Actual checksum: 0x" << std::hex << cal_checksum;
809        }
810
811        if (!tag.empty()) {
812            LOG(FATAL, PANDAFILE) << msg << ", from method: " << tag;
813        } else {
814            LOG(FATAL, PANDAFILE) << msg;
815        }
816    }
817}
818
819}  // namespace panda::panda_file
820