• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #ifndef LIBPANDAFILE_FILE_H
17 #define LIBPANDAFILE_FILE_H
18 
19 #include <array>
20 #include <cstdint>
21 #include <fcntl.h>
22 #include <iostream>
23 #include <memory>
24 #include <string>
25 #include <string_view>
26 
27 #include "helpers.h"
28 #include "os/mem.h"
29 #include "os/filesystem.h"
30 #include "utils/span.h"
31 #include "utils/utf.h"
32 #include "utils/logger.h"
33 
34 namespace panda {
35 struct EntryFileStat;
36 }  // namespace panda
37 
38 namespace panda::panda_file {
39 
40 class PandaCache;
41 
42 #define XPM_PROC_LENGTH 50
43 #define PROC_SELF_XPM_REGION_PATH "/proc/self/xpm_region"
44 /*
45  * EntityPairHeader Describes pair for hash value of class's descriptor and its entity id offset,
46  * used to quickly find class by its descriptor.
47  */
48 struct EntityPairHeader {
49     uint32_t descriptor_hash;
50     uint32_t entity_id_offset;
51     uint32_t next_pos;
52 };
53 
54 class File {
55 public:
56     using Index = uint16_t;
57     using Index32 = uint32_t;
58 
59     static constexpr size_t MAGIC_SIZE = 8;
60     static constexpr size_t VERSION_SIZE = 4;
61     static const std::array<uint8_t, MAGIC_SIZE> MAGIC;
62     static constexpr std::array<uint8_t, VERSION_SIZE> STATIC_VERSION = {0, 0, 0, 5};
63 
64     struct Header {
65         std::array<uint8_t, MAGIC_SIZE> magic;
66         uint32_t checksum;
67         std::array<uint8_t, VERSION_SIZE> version;
68         uint32_t file_size;
69         uint32_t foreign_off;
70         uint32_t foreign_size;
71         uint32_t num_classes;
72         uint32_t class_idx_off;
73         uint32_t num_lnps;
74         uint32_t lnp_idx_off;
75         uint32_t num_literalarrays;
76         uint32_t literalarray_idx_off;
77         uint32_t num_indexes;
78         uint32_t index_section_off;
79     };
80 
81     struct IndexHeader {
82         uint32_t start;
83         uint32_t end;
84         uint32_t class_idx_size;
85         uint32_t class_idx_off;
86         uint32_t method_idx_size;
87         uint32_t method_idx_off;
88         uint32_t field_idx_size;
89         uint32_t field_idx_off;
90         uint32_t proto_idx_size;
91         uint32_t proto_idx_off;
92     };
93 
94     struct StringData {
StringDataStringData95         StringData(uint32_t len, const uint8_t *d) : utf16_length(len), is_ascii(false), data(d) {}
96         StringData() = default;
97         uint32_t utf16_length;  // NOLINT(misc-non-private-member-variables-in-classes)
98         bool is_ascii;          // NOLINT(misc-non-private-member-variables-in-classes)
99         const uint8_t *data;    // NOLINT(misc-non-private-member-variables-in-classes)
100     };
101 
102     // NOLINTNEXTLINE(cppcoreguidelines-special-member-functions, hicpp-special-member-functions)
103     class EntityId {
104     public:
EntityId(uint32_t offset)105         explicit constexpr EntityId(uint32_t offset) : offset_(offset) {}
106 
107         EntityId() = default;
108 
109         ~EntityId() = default;
110 
IsValid()111         bool IsValid() const
112         {
113             return offset_ > sizeof(Header);
114         }
115 
GetOffset()116         uint32_t GetOffset() const
117         {
118             return offset_;
119         }
120 
GetSize()121         static constexpr size_t GetSize()
122         {
123             return sizeof(uint32_t);
124         }
125 
126         friend bool operator<(const EntityId &l, const EntityId &r)
127         {
128             return l.offset_ < r.offset_;
129         }
130 
131         friend bool operator==(const EntityId &l, const EntityId &r)
132         {
133             return l.offset_ == r.offset_;
134         }
135 
136         friend std::ostream &operator<<(std::ostream &stream, const EntityId &id)
137         {
138             return stream << id.offset_;
139         }
140 
141     private:
142         uint32_t offset_ {0};
143     };
144 
145     enum OpenMode { READ_ONLY, READ_WRITE, WRITE_ONLY };
146 
147     StringData GetStringData(EntityId id) const;
148     EntityId GetLiteralArraysId() const;
149 
150     EntityId GetClassId(const uint8_t *mutf8_name) const;
151 
152     EntityId GetClassIdFromClassHashTable(const uint8_t *mutf8_name) const;
153 
GetHeader()154     const Header *GetHeader() const
155     {
156         return reinterpret_cast<const Header *>(GetBase());
157     }
158 
GetBase()159     const uint8_t *GetBase() const
160     {
161         return reinterpret_cast<const uint8_t *>(base_.Get());
162     }
163 
GetPtr()164     const os::mem::ConstBytePtr &GetPtr() const
165     {
166         return base_;
167     }
168 
IsExternal(EntityId id)169     bool IsExternal(EntityId id) const
170     {
171         const Header *header = GetHeader();
172         uint32_t foreign_begin = header->foreign_off;
173         uint32_t foreign_end = foreign_begin + header->foreign_size;
174         return id.GetOffset() >= foreign_begin && id.GetOffset() < foreign_end;
175     }
176 
GetIdFromPointer(const uint8_t * ptr)177     EntityId GetIdFromPointer(const uint8_t *ptr) const
178     {
179         return EntityId(ptr - GetBase());
180     }
181 
GetSpanFromId(EntityId id)182     Span<const uint8_t> GetSpanFromId(EntityId id) const
183     {
184         const Header *header = GetHeader();
185         Span file(GetBase(), header->file_size);
186         ThrowIfWithCheck(!id.IsValid() || id.GetOffset() >= file.size(), File::INVALID_FILE_OFFSET,
187                          File::GET_SPAN_FROM_ID);
188         return file.Last(file.size() - id.GetOffset());
189     }
190 
GetClasses()191     Span<const uint32_t> GetClasses() const
192     {
193         const Header *header = GetHeader();
194         Span file(GetBase(), header->file_size);
195         Span class_idx_data = file.SubSpan(header->class_idx_off, header->num_classes * sizeof(uint32_t));
196         return Span(reinterpret_cast<const uint32_t *>(class_idx_data.data()), header->num_classes);
197     }
198 
GetLiteralArrays()199     Span<const uint32_t> GetLiteralArrays() const
200     {
201         const Header *header = GetHeader();
202         Span file(GetBase(), header->file_size);
203         Span litarr_idx_data = file.SubSpan(header->literalarray_idx_off, header->num_literalarrays * sizeof(uint32_t));
204         return Span(reinterpret_cast<const uint32_t *>(litarr_idx_data.data()), header->num_literalarrays);
205     }
206 
GetIndexHeaders()207     Span<const IndexHeader> GetIndexHeaders() const
208     {
209         const Header *header = GetHeader();
210         Span file(GetBase(), header->file_size);
211         auto sp = file.SubSpan(header->index_section_off, header->num_indexes * sizeof(IndexHeader));
212         return Span(reinterpret_cast<const IndexHeader *>(sp.data()), header->num_indexes);
213     }
214 
GetIndexHeader(EntityId id)215     const IndexHeader *GetIndexHeader(EntityId id) const
216     {
217         if (UNLIKELY(!id.IsValid() || id.GetOffset() >= GetHeader()->file_size)) {
218             return nullptr;
219         }
220         auto headers = GetIndexHeaders();
221         auto offset = id.GetOffset();
222         for (const auto &header : headers) {
223             if (header.start <= offset && offset < header.end) {
224                 return &header;
225             }
226         }
227         return nullptr;
228     }
229 
GetClassIndex(const IndexHeader * index_header)230     Span<const EntityId> GetClassIndex(const IndexHeader *index_header) const
231     {
232         ThrowIfWithCheck(index_header == nullptr, File::NULL_INDEX_HEADER, File::GET_CLASS_INDEX);
233         auto *header = GetHeader();
234         Span file(GetBase(), header->file_size);
235         ASSERT(index_header != nullptr);
236         auto class_idx_size = index_header->class_idx_size * EntityId::GetSize();
237         ThrowIfWithCheck(index_header->class_idx_off > header->file_size || class_idx_size > header->file_size ||
238             index_header->class_idx_off > header->file_size - class_idx_size, File::INVALID_INDEX_HEADER,
239             File::GET_CLASS_INDEX);
240         auto sp = file.SubSpan(index_header->class_idx_off, index_header->class_idx_size * EntityId::GetSize());
241         return Span(reinterpret_cast<const EntityId *>(sp.data()), index_header->class_idx_size);
242     }
243 
GetClassIndex(EntityId id)244     Span<const EntityId> GetClassIndex(EntityId id) const
245     {
246         auto *index_header = GetIndexHeader(id);
247         return GetClassIndex(index_header);
248     }
249 
GetMethodIndex(const IndexHeader * index_header)250     Span<const EntityId> GetMethodIndex(const IndexHeader *index_header) const
251     {
252         ThrowIfWithCheck(index_header == nullptr, File::NULL_INDEX_HEADER, File::GET_METHOD_INDEX);
253         auto *header = GetHeader();
254         Span file(GetBase(), header->file_size);
255         ASSERT(index_header != nullptr);
256         // NOLINTNEXTLINE(clang-analyzer-core.NullDereference)
257         auto method_idx_size = index_header->method_idx_size * EntityId::GetSize();
258         ThrowIfWithCheck(index_header->method_idx_off > header->file_size || method_idx_size > header->file_size ||
259             index_header->method_idx_off > header->file_size - method_idx_size, File::INVALID_INDEX_HEADER,
260             File::GET_METHOD_INDEX);
261         auto sp = file.SubSpan(index_header->method_idx_off, index_header->method_idx_size * EntityId::GetSize());
262         return Span(reinterpret_cast<const EntityId *>(sp.data()), index_header->method_idx_size);
263     }
264 
GetMethodIndex(EntityId id)265     Span<const EntityId> GetMethodIndex(EntityId id) const
266     {
267         auto *index_header = GetIndexHeader(id);
268         return GetMethodIndex(index_header);
269     }
270 
GetFieldIndex(const IndexHeader * index_header)271     Span<const EntityId> GetFieldIndex(const IndexHeader *index_header) const
272     {
273         ThrowIfWithCheck(index_header == nullptr, File::NULL_INDEX_HEADER, File::GET_FIELD_INDEX);
274         auto *header = GetHeader();
275         Span file(GetBase(), header->file_size);
276         ASSERT(index_header != nullptr);
277         auto field_idx_size = index_header->field_idx_size * EntityId::GetSize();
278         ThrowIfWithCheck(index_header->field_idx_off > header->file_size || field_idx_size > header->file_size ||
279             index_header->field_idx_off > header->file_size - field_idx_size, File::INVALID_INDEX_HEADER,
280             File::GET_FIELD_INDEX);
281         auto sp = file.SubSpan(index_header->field_idx_off, index_header->field_idx_size * EntityId::GetSize());
282         return Span(reinterpret_cast<const EntityId *>(sp.data()), index_header->field_idx_size);
283     }
284 
GetFieldIndex(EntityId id)285     Span<const EntityId> GetFieldIndex(EntityId id) const
286     {
287         auto *index_header = GetIndexHeader(id);
288         return GetFieldIndex(index_header);
289     }
290 
GetProtoIndex(const IndexHeader * index_header)291     Span<const EntityId> GetProtoIndex(const IndexHeader *index_header) const
292     {
293         ThrowIfWithCheck(index_header == nullptr, File::NULL_INDEX_HEADER, File::GET_PROTO_INDEX);
294         auto *header = GetHeader();
295         Span file(GetBase(), header->file_size);
296         ASSERT(index_header != nullptr);
297         auto proto_idx_size = index_header->proto_idx_size * EntityId::GetSize();
298         ThrowIfWithCheck(index_header->proto_idx_off > header->file_size || proto_idx_size > header->file_size ||
299             index_header->proto_idx_off > header->file_size - proto_idx_size, File::INVALID_INDEX_HEADER,
300             File::GET_PROTO_INDEX);
301         auto sp = file.SubSpan(index_header->proto_idx_off, index_header->proto_idx_size * EntityId::GetSize());
302         return Span(reinterpret_cast<const EntityId *>(sp.data()), index_header->proto_idx_size);
303     }
304 
GetProtoIndex(EntityId id)305     Span<const EntityId> GetProtoIndex(EntityId id) const
306     {
307         auto *index_header = GetIndexHeader(id);
308         return GetProtoIndex(index_header);
309     }
310 
GetLineNumberProgramIndex()311     Span<const EntityId> GetLineNumberProgramIndex() const
312     {
313         const Header *header = GetHeader();
314         Span file(GetBase(), header->file_size);
315         Span lnp_idx_data = file.SubSpan(header->lnp_idx_off, header->num_lnps * EntityId::GetSize());
316         return Span(reinterpret_cast<const EntityId *>(lnp_idx_data.data()), header->num_lnps);
317     }
318 
ResolveClassIndex(EntityId id,Index idx)319     EntityId ResolveClassIndex(EntityId id, Index idx) const
320     {
321         auto index = GetClassIndex(id);
322         if (UNLIKELY(idx >= index.Size())) {
323             return EntityId();
324         }
325         return index[idx];
326     }
327 
ResolveMethodIndex(EntityId id,Index idx)328     EntityId ResolveMethodIndex(EntityId id, Index idx) const
329     {
330         auto index = GetMethodIndex(id);
331         if (UNLIKELY(idx >= index.Size())) {
332             return EntityId();
333         }
334         return index[idx];
335     }
336 
ResolveOffsetByIndex(EntityId id,Index idx)337     EntityId ResolveOffsetByIndex(EntityId id, Index idx) const
338     {
339         auto index = GetMethodIndex(id);
340         if (UNLIKELY(idx >= index.Size())) {
341             return EntityId();
342         }
343         return index[idx];
344     }
345 
ResolveFieldIndex(EntityId id,Index idx)346     EntityId ResolveFieldIndex(EntityId id, Index idx) const
347     {
348         auto index = GetFieldIndex(id);
349         if (UNLIKELY(idx >= index.Size())) {
350             return EntityId();
351         }
352         return index[idx];
353     }
354 
ResolveProtoIndex(EntityId id,Index idx)355     EntityId ResolveProtoIndex(EntityId id, Index idx) const
356     {
357         auto index = GetProtoIndex(id);
358         if (UNLIKELY(idx >= index.Size())) {
359             return EntityId();
360         }
361         return index[idx];
362     }
363 
ResolveLineNumberProgramIndex(Index32 idx)364     EntityId ResolveLineNumberProgramIndex(Index32 idx) const
365     {
366         auto index = GetLineNumberProgramIndex();
367         if (UNLIKELY(idx >= index.Size())) {
368             return EntityId();
369         }
370         return index[idx];
371     }
372 
GetFilename()373     const std::string &GetFilename() const
374     {
375         return FILENAME;
376     }
377 
GetPandaCache()378     PandaCache *GetPandaCache() const
379     {
380 #ifdef ENABLE_FULL_FILE_FIELDS
381         return panda_cache_.get();
382 #else
383         LOG(WARNING, PANDAFILE) << "Not Support GetPandaCache from ohos side.";
384         return nullptr;
385 #endif
386     }
387 
GetFilenameHash()388     uint32_t GetFilenameHash() const
389     {
390         return FILENAME_HASH;
391     }
392 
393     // note: intentionally returns uint64_t instead of the field type due to usage
GetUniqId()394     uint64_t GetUniqId() const
395     {
396         return UNIQ_ID;
397     }
398 
GetFullFileName()399     const std::string &GetFullFileName() const
400     {
401 #ifdef ENABLE_FULL_FILE_FIELDS
402         return FULL_FILENAME;
403 #else
404         LOG(FATAL, PANDAFILE) << "Not Support GetFullFileName from ohos side.";
405         return FILENAME;
406 #endif
407     }
408 
GetFileBaseOffset()409     static constexpr uint32_t GetFileBaseOffset()
410     {
411         return MEMBER_OFFSET(File, base_);
412     }
413 
GetClassHashTable()414     Span<const panda::panda_file::EntityPairHeader> GetClassHashTable() const
415     {
416         return class_hash_table_;
417     }
418 
419     static uint32_t CalcFilenameHash(const std::string &filename);
420 
421     static std::unique_ptr<const File> Open(std::string_view filename, OpenMode open_mode = READ_ONLY);
422 
423     static std::unique_ptr<const File> OpenFromMemory(os::mem::ConstBytePtr &&ptr);
424 
425     static std::unique_ptr<const File> OpenFromMemory(os::mem::ConstBytePtr &&ptr, std::string_view filename);
426 
427     static std::unique_ptr<const File> OpenUncompressedArchive(int fd, const std::string_view &filename, size_t size,
428                                                                uint32_t offset, OpenMode open_mode = READ_ONLY);
429 
SetClassHashTable(panda::Span<const panda::panda_file::EntityPairHeader> class_hash_table)430     void SetClassHashTable(panda::Span<const panda::panda_file::EntityPairHeader> class_hash_table) const
431     {
432         class_hash_table_ = class_hash_table;
433     }
434 
435     bool ValidateChecksum(uint32_t *cal_checksum_out = nullptr) const;
436 
437     void ThrowIfWithCheck(bool cond, const std::string_view& msg, const std::string_view& tag = "") const;
438 
439     static constexpr const char *INVALID_FILE_OFFSET = "Invalid file offset";
440     static constexpr const char *NULL_INDEX_HEADER = "index_header is null";
441     static constexpr const char *INVALID_INDEX_HEADER = "index_header is invalid";
442 
443     static constexpr const char *GET_CLASS_INDEX = "GetClassIndex";
444     static constexpr const char *GET_METHOD_INDEX = "GetMethodIndex";
445     static constexpr const char *GET_FIELD_INDEX = "GetFieldIndex";
446     static constexpr const char *GET_PROTO_INDEX = "GetProtoIndex";
447 
448     static constexpr const char *ANNOTATION_DATA_ACCESSOR = "AnnotationDataAccessor";
449     static constexpr const char *CLASS_DATA_ACCESSOR = "ClassDataAccessor";
450     static constexpr const char *CODE_DATA_ACCESSOR = "CodeDataAccessor";
451     static constexpr const char *FIELD_DATA_ACCESSOR = "FieldDataAccessor";
452     static constexpr const char *GET_SPAN_FROM_ID = "GetSpanFromId";
453 
454     ~File();
455 
456     NO_COPY_SEMANTIC(File);
457     NO_MOVE_SEMANTIC(File);
458 
459 private:
460     File(std::string filename, os::mem::ConstBytePtr &&base);
461 
462     os::mem::ConstBytePtr base_;
463     const std::string FILENAME;
464     const uint32_t FILENAME_HASH;
465 #ifdef ENABLE_FULL_FILE_FIELDS
466     const std::string FULL_FILENAME;
467     std::unique_ptr<PandaCache> panda_cache_;
468 #endif
469     const uint32_t UNIQ_ID;
470     mutable panda::Span<const panda::panda_file::EntityPairHeader> class_hash_table_;
471 };
472 
473 static_assert(File::GetFileBaseOffset() == 0);
474 
475 inline bool operator==(const File::StringData &string_data1, const File::StringData &string_data2)
476 {
477     if (string_data1.utf16_length != string_data2.utf16_length) {
478         return false;
479     }
480 
481     return utf::IsEqual(string_data1.data, string_data2.data);
482 }
483 
484 inline bool operator!=(const File::StringData &string_data1, const File::StringData &string_data2)
485 {
486     return !(string_data1 == string_data2);
487 }
488 
489 inline bool operator<(const File::StringData &string_data1, const File::StringData &string_data2)
490 {
491     if (string_data1.utf16_length == string_data2.utf16_length) {
492         return utf::CompareMUtf8ToMUtf8(string_data1.data, string_data2.data) < 0;
493     }
494 
495     return string_data1.utf16_length < string_data2.utf16_length;
496 }
497 
498 bool CheckSecureMem(uintptr_t mem, size_t size);
499 
500 /*
501  * OpenPandaFileOrZip from location which specicify the name.
502  */
503 std::unique_ptr<const File> OpenPandaFileOrZip(std::string_view location,
504                                                panda_file::File::OpenMode open_mode = panda_file::File::READ_ONLY);
505 
506 /*
507  * OpenPandaFileFromMemory from file buffer.
508  */
509 std::unique_ptr<const File> OpenPandaFileFromMemory(const void *buffer, size_t size, std::string tag = "");
510 
511 /*
512  * OpenPandaFileFromMemory from secure buffer.
513  */
514 std::unique_ptr<const File> OpenPandaFileFromSecureMemory(uint8_t *buffer, size_t size);
515 
516 /*
517  * OpenPandaFile from location which specicify the name.
518  */
519 std::unique_ptr<const File> OpenPandaFile(std::string_view location, std::string_view archive_filename = "",
520                                           panda_file::File::OpenMode open_mode = panda_file::File::READ_ONLY);
521 
522 /*
523  * Check ptr point valid panda file: magic
524  */
525 bool CheckHeader(const os::mem::ConstBytePtr &ptr, const std::string_view &filename = "");
526 void CheckFileVersion(const std::array<uint8_t, File::VERSION_SIZE> &file_version, const std::string_view &filename);
527 
528 enum class PandaFileType : int8_t {
529     FILE_FORMAT_INVALID = -1,
530     FILE_DYNAMIC = 0,
531     FILE_STATIC = 1,
532 };
533 
534 /**
535  * @brief Return the abc file type
536  * @param[in] data : begin pos of abc file
537  * @param[in] size : length of abc file
538  * @return -1 indicates the abc file format invalid
539  *         0 indicates the abc file is dynamic
540  *         1 indicates the abc file is static
541  */
542 PandaFileType GetFileType(const uint8_t *data, int32_t size);
543 
544 // Last version which contains redundance literal array in header
545 constexpr std::array<uint8_t, File::VERSION_SIZE> LAST_CONTAINS_LITERAL_IN_HEADER_VERSION {12, 0, 6, 0};
546 bool ContainsLiteralArrayInHeader(const std::array<uint8_t, File::VERSION_SIZE> &version);
547 
548 // NOLINTNEXTLINE(readability-identifier-naming)
549 extern const char *ARCHIVE_FILENAME;
550 }  // namespace panda::panda_file
551 
552 namespace std {
553 template <>
554 struct hash<panda::panda_file::File::EntityId> {
555     std::size_t operator()(panda::panda_file::File::EntityId id) const
556     {
557         return std::hash<uint32_t> {}(id.GetOffset());
558     }
559 };
560 
561 }  // namespace std
562 
563 #endif  // LIBPANDAFILE_FILE_H
564