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