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
OpenPandaFileFromSecureMemory(uint8_t * buffer,size_t size)301 std::unique_ptr<const File> OpenPandaFileFromSecureMemory(uint8_t *buffer, size_t size)
302 {
303 if (buffer == nullptr) {
304 return nullptr;
305 }
306
307 std::byte *mem = reinterpret_cast<std::byte *>(buffer);
308 os::mem::ConstBytePtr ptr(mem, size, nullptr);
309 if (ptr.Get() == nullptr) {
310 PLOG(ERROR, PANDAFILE) << "Failed to open panda file from secure memory'";
311 return nullptr;
312 }
313
314 std::hash<std::byte *> hash;
315 return panda_file::File::OpenFromMemory(std::move(ptr), std::to_string(hash(mem)));
316 }
317
318 class ClassIdxIterator {
319 public:
320 using value_type = const uint8_t *;
321 using difference_type = std::ptrdiff_t;
322 using pointer = uint32_t *;
323 using reference = uint32_t &;
324 using iterator_category = std::random_access_iterator_tag;
325
ClassIdxIterator(const File & file,const Span<const uint32_t> & span,size_t idx)326 ClassIdxIterator(const File &file, const Span<const uint32_t> &span, size_t idx)
327 : file_(file), span_(span), idx_(idx)
328 {
329 }
330
331 ClassIdxIterator(const ClassIdxIterator &other) = default;
332 ClassIdxIterator(ClassIdxIterator &&other) = default;
333 ~ClassIdxIterator() = default;
334
operator =(const ClassIdxIterator & other)335 ClassIdxIterator &operator=(const ClassIdxIterator &other)
336 {
337 if (&other != this) {
338 idx_ = other.idx_;
339 }
340
341 return *this;
342 }
343
operator =(ClassIdxIterator && other)344 ClassIdxIterator &operator=(ClassIdxIterator &&other) noexcept
345 {
346 idx_ = other.idx_;
347 return *this;
348 }
349
operator +=(size_t n)350 ClassIdxIterator &operator+=(size_t n)
351 {
352 idx_ += n;
353 return *this;
354 }
355
operator -=(size_t n)356 ClassIdxIterator &operator-=(size_t n)
357 {
358 idx_ -= n;
359 return *this;
360 }
361
operator ++()362 ClassIdxIterator &operator++()
363 {
364 ++idx_;
365 return *this;
366 }
367
operator --()368 ClassIdxIterator &operator--()
369 {
370 --idx_;
371 return *this;
372 }
373
operator -(const ClassIdxIterator & other)374 difference_type operator-(const ClassIdxIterator &other)
375 {
376 return idx_ - other.idx_;
377 }
378
operator *() const379 value_type operator*() const
380 {
381 uint32_t id = span_[idx_];
382 return file_.GetStringData(File::EntityId(id)).data;
383 }
384
IsValid() const385 bool IsValid() const
386 {
387 return idx_ < span_.Size();
388 }
389
GetId() const390 uint32_t GetId() const
391 {
392 return span_[idx_];
393 }
394
Begin(const File & file,const Span<const uint32_t> & span)395 static ClassIdxIterator Begin(const File &file, const Span<const uint32_t> &span)
396 {
397 return ClassIdxIterator(file, span, 0);
398 }
399
End(const File & file,const Span<const uint32_t> & span)400 static ClassIdxIterator End(const File &file, const Span<const uint32_t> &span)
401 {
402 return ClassIdxIterator(file, span, span.Size());
403 }
404
405 private:
406 const File &file_;
407 const Span<const uint32_t> &span_;
408 size_t idx_;
409 };
410
File(std::string filename,os::mem::ConstBytePtr && base)411 File::File(std::string filename, os::mem::ConstBytePtr &&base)
412 : base_(std::forward<os::mem::ConstBytePtr>(base)),
413 FILENAME(std::move(filename)),
414 FILENAME_HASH(CalcFilenameHash(FILENAME)),
415 #ifdef ENABLE_FULL_FILE_FIELDS
416 FULL_FILENAME(os::GetAbsolutePath(FILENAME)),
417 panda_cache_(std::make_unique<PandaCache>()),
418 #endif
419 UNIQ_ID(merge_hashes(FILENAME_HASH, GetHash32(reinterpret_cast<const uint8_t *>(GetHeader()), sizeof(Header))))
420 {
421 }
422
~File()423 File::~File()
424 {
425 AnonMemSet::GetInstance().Remove(FILENAME);
426 }
427
VersionToString(const std::array<uint8_t,File::VERSION_SIZE> & array)428 inline std::string VersionToString(const std::array<uint8_t, File::VERSION_SIZE> &array)
429 {
430 std::stringstream ss;
431
432 for (size_t i = 0; i < File::VERSION_SIZE - 1; ++i) {
433 ss << static_cast<int>(array[i]);
434 ss << ".";
435 }
436 ss << static_cast<int>(array[File::VERSION_SIZE - 1]);
437
438 return ss.str();
439 }
440
441 // We can't use default std::array's comparision operators and need to implement
442 // 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)443 inline int CompareVersions(const std::array<uint8_t, File::VERSION_SIZE> &lhs,
444 const std::array<uint8_t, File::VERSION_SIZE> &rhs)
445 {
446 for (size_t i = 0; i < File::VERSION_SIZE; i++) {
447 if (lhs[i] == rhs[i]) {
448 continue;
449 }
450 return lhs[i] - rhs[i];
451 }
452 return 0;
453 }
454
operator <(const std::array<uint8_t,File::VERSION_SIZE> & lhs,const std::array<uint8_t,File::VERSION_SIZE> & rhs)455 inline bool operator<(const std::array<uint8_t, File::VERSION_SIZE> &lhs,
456 const std::array<uint8_t, File::VERSION_SIZE> &rhs)
457 {
458 return CompareVersions(lhs, rhs) < 0;
459 }
460
operator >(const std::array<uint8_t,File::VERSION_SIZE> & lhs,const std::array<uint8_t,File::VERSION_SIZE> & rhs)461 inline bool operator>(const std::array<uint8_t, File::VERSION_SIZE> &lhs,
462 const std::array<uint8_t, File::VERSION_SIZE> &rhs)
463 {
464 return CompareVersions(lhs, rhs) > 0;
465 }
466
467 /* static */
Open(std::string_view filename,OpenMode open_mode)468 std::unique_ptr<const File> File::Open(std::string_view filename, OpenMode open_mode)
469 {
470 trace::ScopedTrace scoped_trace("Open panda file " + std::string(filename));
471 os::file::Mode mode = GetMode(open_mode);
472 os::file::File file = os::file::Open(filename, mode);
473 if (!file.IsValid()) {
474 PLOG(ERROR, PANDAFILE) << "Failed to open panda file '" << filename << "'";
475 return nullptr;
476 }
477
478 os::file::FileHolder fh_holder(file);
479
480 auto res = file.GetFileSize();
481 if (!res) {
482 PLOG(ERROR, PANDAFILE) << "Failed to get size of panda file '" << filename << "'";
483 return nullptr;
484 }
485
486 size_t size = res.Value();
487 if (size < sizeof(File::Header)) {
488 LOG(ERROR, PANDAFILE) << "Invalid panda file '" << filename << "' - has not header";
489 return nullptr;
490 }
491
492 os::mem::ConstBytePtr ptr = os::mem::MapFile(file, GetProt(open_mode), os::mem::MMAP_FLAG_PRIVATE, size).ToConst();
493 if (ptr.Get() == nullptr) {
494 PLOG(ERROR, PANDAFILE) << "Failed to map panda file '" << filename << "'";
495 return nullptr;
496 }
497
498 if (!CheckHeader(ptr, filename)) {
499 return nullptr;
500 }
501
502 return std::unique_ptr<File>(new File(filename.data(), std::move(ptr)));
503 }
504
OpenUncompressedArchive(int fd,const std::string_view & filename,size_t size,uint32_t offset,OpenMode open_mode)505 std::unique_ptr<const File> File::OpenUncompressedArchive(int fd, const std::string_view &filename, size_t size,
506 uint32_t offset, OpenMode open_mode)
507 {
508 trace::ScopedTrace scoped_trace("Open panda file " + std::string(filename));
509 auto file = os::file::File(fd);
510 if (!file.IsValid()) {
511 PLOG(ERROR, PANDAFILE) << "OpenUncompressedArchive: Failed to open panda file '" << filename << "'";
512 return nullptr;
513 }
514
515 if (size < sizeof(File::Header)) {
516 LOG(ERROR, PANDAFILE) << "Invalid panda file size '" << filename << "'";
517 return nullptr;
518 }
519 LOG(DEBUG, PANDAFILE) << " size=" << size << " offset=" << offset << " " << filename;
520
521 os::mem::ConstBytePtr ptr =
522 os::mem::MapFile(file, GetProt(open_mode), os::mem::MMAP_FLAG_PRIVATE, size, offset).ToConst();
523 if (ptr.Get() == nullptr) {
524 PLOG(ERROR, PANDAFILE) << "Failed to map panda file '" << filename << "'";
525 return nullptr;
526 }
527 if (!CheckHeader(ptr, filename)) {
528 return nullptr;
529 }
530
531 return std::unique_ptr<File>(new File(filename.data(), std::move(ptr)));
532 }
533
534 template <typename T = uint32_t>
CheckHeaderElementOffset(size_t offset,size_t number,size_t file_size)535 bool CheckHeaderElementOffset(size_t offset, size_t number, size_t file_size)
536 {
537 auto number_size = number * sizeof(T);
538 if (offset > file_size || number_size > file_size || offset > file_size - number_size) {
539 return false;
540 }
541 return true;
542 }
543
CheckHeader(const os::mem::ConstBytePtr & ptr,const std::string_view & filename)544 bool CheckHeader(const os::mem::ConstBytePtr &ptr, const std::string_view &filename)
545 {
546 if (ptr.Get() == nullptr || ptr.GetSize() < sizeof(File::Header)) {
547 LOG(ERROR, PANDAFILE) << "Invalid panda file '" << filename << "'";
548 return false;
549 }
550 auto header = reinterpret_cast<const File::Header *>(reinterpret_cast<uintptr_t>(ptr.Get()));
551 if (header->magic != File::MAGIC) {
552 LOG(ERROR, PANDAFILE) << "Invalid magic number '";
553 return false;
554 }
555
556 auto file_version = header->version;
557 // skip isa version check for version number of 10.0.0.0 temporarily
558 constexpr std::array<uint8_t, File::VERSION_SIZE> tempVersion = {10, 0, 0, 0};
559 if ((file_version < minVersion || file_version > version) && !(file_version == tempVersion)) {
560 LOG(ERROR, PANDAFILE) << "Unable to open file '" << filename << "' with bytecode version "
561 << VersionToString(file_version);
562 if (file_version < minVersion) {
563 LOG(ERROR, PANDAFILE) << "Minimum supported version is " << VersionToString(minVersion)
564 << ". Please try runtime of former version or generate byte code with SDK tools of supported version";
565 } else {
566 LOG(ERROR, PANDAFILE) << "Maximum supported version is " << VersionToString(version)
567 << ". Please upgrade runtime to supported version or generate byte code with former SDK tools";
568 }
569 return false;
570 }
571
572 if (header->file_size < sizeof(File::Header) || header->file_size > ptr.GetSize()) {
573 LOG(ERROR, PANDAFILE) << "Invalid panda file size " << header->file_size;
574 return false;
575 }
576
577 if (!CheckHeaderElementOffset<uint8_t>(header->foreign_off, header->foreign_size, header->file_size)) {
578 LOG(ERROR, PANDAFILE) << "Invalid panda file foreign_off " << header->foreign_off <<
579 " or foreign_size " << header->foreign_size;
580 return false;
581 }
582
583 if (!CheckHeaderElementOffset(header->class_idx_off, header->num_classes, header->file_size)) {
584 LOG(ERROR, PANDAFILE) << "Invalid panda file class_idx_off " << header->class_idx_off <<
585 " or num_classes " << header->num_classes;
586 return false;
587 }
588
589 if (!CheckHeaderElementOffset(header->lnp_idx_off, header->num_lnps, header->file_size)) {
590 LOG(ERROR, PANDAFILE) << "Invalid panda file lnp_idx_off " << header->lnp_idx_off <<
591 " or num_lnps " << header->num_lnps;
592 return false;
593 }
594
595 if (!CheckHeaderElementOffset(header->literalarray_idx_off, header->num_literalarrays, header->file_size)) {
596 LOG(ERROR, PANDAFILE) << "Invalid panda file literalarray_idx_off " << header->literalarray_idx_off <<
597 " or num_literalarrays " << header->num_literalarrays;
598 return false;
599 }
600
601 if (!CheckHeaderElementOffset<File::IndexHeader>(header->index_section_off, header->num_indexes,
602 header->file_size)) {
603 LOG(ERROR, PANDAFILE) << "Invalid panda file index_section_off " << header->index_section_off <<
604 " or num_indexes " << header->num_indexes;
605 return false;
606 }
607
608 return true;
609 }
610
611 /* static */
OpenFromMemory(os::mem::ConstBytePtr && ptr)612 std::unique_ptr<const File> File::OpenFromMemory(os::mem::ConstBytePtr &&ptr)
613 {
614 if (!CheckHeader(ptr, std::string_view())) {
615 return nullptr;
616 }
617
618 return std::unique_ptr<File>(new File("", std::forward<os::mem::ConstBytePtr>(ptr)));
619 }
620
621 /* static */
OpenFromMemory(os::mem::ConstBytePtr && ptr,std::string_view filename)622 std::unique_ptr<const File> File::OpenFromMemory(os::mem::ConstBytePtr &&ptr, std::string_view filename)
623 {
624 trace::ScopedTrace scoped_trace("Open panda file from RAM " + std::string(filename));
625
626 if (!CheckHeader(ptr, filename)) {
627 return nullptr;
628 }
629
630 return std::unique_ptr<File>(new File(filename.data(), std::forward<os::mem::ConstBytePtr>(ptr)));
631 }
632
GetClassId(const uint8_t * mutf8_name) const633 File::EntityId File::GetClassId(const uint8_t *mutf8_name) const
634 {
635 auto class_hash_table = GetClassHashTable();
636 if (!class_hash_table.empty()) {
637 return GetClassIdFromClassHashTable(mutf8_name);
638 }
639
640 auto class_idx = GetClasses();
641
642 auto it = std::lower_bound(ClassIdxIterator::Begin(*this, class_idx), ClassIdxIterator::End(*this, class_idx),
643 mutf8_name, utf::Mutf8Less());
644 if (!it.IsValid()) {
645 return EntityId();
646 }
647
648 if (utf::CompareMUtf8ToMUtf8(mutf8_name, *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->literalarray_idx_off);
664 }
665
GetClassIdFromClassHashTable(const uint8_t * mutf8_name) const666 File::EntityId File::GetClassIdFromClassHashTable(const uint8_t *mutf8_name) const
667 {
668 auto class_hash_table = GetClassHashTable();
669 auto hash = GetHash32String(mutf8_name);
670 auto pos = hash & (class_hash_table.size() - 1);
671 auto entity_pair = &class_hash_table[pos];
672
673 if (entity_pair->descriptor_hash % class_hash_table.size() != pos) {
674 return File::EntityId();
675 }
676
677 while (true) {
678 if (hash == entity_pair->descriptor_hash) {
679 auto entity_id = File::EntityId(entity_pair->entity_id_offset);
680 auto descriptor = GetStringData(entity_id).data;
681 if (entity_id.IsValid() && utf::CompareMUtf8ToMUtf8(descriptor, mutf8_name) == 0) {
682 return entity_id;
683 }
684 }
685 if (entity_pair->next_pos == 0) {
686 break;
687 }
688 entity_pair = &class_hash_table[entity_pair->next_pos - 1];
689 }
690
691 return File::EntityId();
692 }
693
694 } // namespace panda::panda_file
695