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