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