| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #ifndef ART_LIBDEXFILE_DEX_DEX_FILE_LOADER_H_ |
| #define ART_LIBDEXFILE_DEX_DEX_FILE_LOADER_H_ |
| |
| #include <cstdint> |
| #include <functional> |
| #include <memory> |
| #include <optional> |
| #include <string> |
| #include <string_view> |
| #include <vector> |
| |
| #include "base/os.h" |
| #include "base/unix_file/fd_file.h" |
| #include "dex_file.h" |
| |
| namespace art { |
| |
| class MemMap; |
| class OatDexFile; |
| class ZipArchive; |
| |
| enum class DexFileLoaderErrorCode { |
| kNoError, |
| kEntryNotFound, |
| kExtractToMemoryError, |
| kDexFileError, |
| kMakeReadOnlyError, |
| kVerifyError |
| }; |
| |
| // Class that is used to open dex files and deal with corresponding multidex and location logic. |
| class DexFileLoader { |
| public: |
| // name of the DexFile entry within a zip archive |
| static constexpr const char* kClassesDex = "classes.dex"; |
| |
| // The separator character in MultiDex locations. |
| static constexpr char kMultiDexSeparator = '!'; |
| |
| // Return true if the magic is valid for dex or cdex. |
| static bool IsMagicValid(uint32_t magic); |
| static bool IsMagicValid(const uint8_t* magic); |
| |
| // Return true if the corresponding version and magic is valid. |
| static bool IsVersionAndMagicValid(const uint8_t* magic); |
| |
| // Check whether a location denotes a multidex dex file. This is a very simple check: returns |
| // whether the string contains the separator character. |
| static bool IsMultiDexLocation(std::string_view location); |
| |
| // Return the name of the index-th classes.dex in a multidex zip file. This is classes.dex for |
| // index == 0, and classes{index + 1}.dex else. |
| static std::string GetMultiDexClassesDexName(size_t index); |
| |
| // Return the (possibly synthetic) dex location for a multidex entry. This is dex_location for |
| // index == 0, and dex_location + multi-dex-separator + GetMultiDexClassesDexName(index) else. |
| static std::string GetMultiDexLocation(size_t index, const char* dex_location); |
| |
| // Returns combined checksum of one or more dex files (one checksum for the whole multidex set). |
| // |
| // This uses the source path provided to DexFileLoader constructor. |
| // |
| // Returns false on error. Sets *checksum to nullopt for an empty set. |
| bool GetMultiDexChecksum(/*out*/ std::optional<uint32_t>* checksum, |
| /*out*/ std::string* error_msg, |
| /*out*/ bool* only_contains_uncompressed_dex = nullptr); |
| |
| // Returns combined checksum of one or more dex files (one checksum for the whole multidex set). |
| // |
| // This uses already open dex files. |
| // |
| // It starts iteration at index 'i', which must be a primary dex file, |
| // and it sets 'i' to the next primary dex file or to end of the array. |
| template <typename DexFilePtrVector> // array|vector<unique_ptr|DexFile|OatDexFile*>. |
| static uint32_t GetMultiDexChecksum(const DexFilePtrVector& dex_files, |
| /*inout*/ size_t* i) { |
| CHECK_LT(*i, dex_files.size()) << "No dex files"; |
| std::optional<uint32_t> checksum; |
| for (; *i < dex_files.size(); ++(*i)) { |
| const auto* dex_file = &*dex_files[*i]; |
| bool is_primary_dex = !IsMultiDexLocation(dex_file->GetLocation().c_str()); |
| if (!checksum.has_value()) { // First dex file. |
| CHECK(is_primary_dex) << dex_file->GetLocation(); // Expect primary dex. |
| } else if (is_primary_dex) { // Later dex file. |
| break; // Found another primary dex file, terminate iteration. |
| } |
| if (!is_primary_dex && dex_file->GetDexVersion() >= DexFile::kDexContainerVersion) { |
| if (dex_file->GetLocationChecksum() == dex_files[*i - 1]->GetLocationChecksum() + 1) { |
| continue; |
| } |
| } |
| checksum = checksum.value_or(kEmptyMultiDexChecksum) ^ dex_file->GetLocationChecksum(); |
| } |
| CHECK(checksum.has_value()); |
| return checksum.value(); |
| } |
| |
| // Calculate checksum of dex files in a vector, starting at index 0. |
| // It will CHECK that the whole vector is consumed (i.e. there is just one primary dex file). |
| template <typename DexFilePtrVector> |
| static uint32_t GetMultiDexChecksum(const DexFilePtrVector& dex_files) { |
| size_t i = 0; |
| uint32_t checksum = GetMultiDexChecksum(dex_files, &i); |
| CHECK_EQ(i, dex_files.size()); |
| return checksum; |
| } |
| |
| // Non-zero initial value for multi-dex to catch bugs if multi-dex checksum is compared |
| // directly to DexFile::GetLocationChecksum without going through GetMultiDexChecksum. |
| static constexpr uint32_t kEmptyMultiDexChecksum = 1; |
| |
| // Returns the canonical form of the given dex location. |
| // |
| // There are different flavors of "dex locations" as follows: |
| // the file name of a dex file: |
| // The actual file path that the dex file has on disk. |
| // dex_location: |
| // This acts as a key for the class linker to know which dex file to load. |
| // It may correspond to either an old odex file or a particular dex file |
| // inside an oat file. In the first case it will also match the file name |
| // of the dex file. In the second case (oat) it will include the file name |
| // and possibly some multidex annotation to uniquely identify it. |
| // canonical_dex_location: |
| // the dex_location where its file name part has been made canonical. |
| static std::string GetDexCanonicalLocation(const char* dex_location); |
| |
| // For normal dex files, location and base location coincide. If a dex file is part of a multidex |
| // archive, the base location is the name of the originating jar/apk, stripped of any internal |
| // classes*.dex path. |
| static std::string GetBaseLocation(const char* location) { |
| const char* pos = strrchr(location, kMultiDexSeparator); |
| return (pos == nullptr) ? location : std::string(location, pos - location); |
| } |
| |
| static std::string GetBaseLocation(const std::string& location) { |
| return GetBaseLocation(location.c_str()); |
| } |
| |
| // Returns the '!classes*.dex' part of the dex location. Returns an empty |
| // string if there is no multidex suffix for the given location. |
| // The kMultiDexSeparator is included in the returned suffix. |
| static std::string GetMultiDexSuffix(const std::string& location) { |
| size_t pos = location.rfind(kMultiDexSeparator); |
| return (pos == std::string::npos) ? std::string() : location.substr(pos); |
| } |
| |
| DexFileLoader(const char* filename, const File* file, const std::string& location) |
| : filename_(filename), file_(file), location_(location) { |
| CHECK(file != nullptr); // Must be non-null, but may be invalid. |
| } |
| |
| DexFileLoader(std::shared_ptr<DexFileContainer> container, const std::string& location) |
| : root_container_(std::move(container)), location_(location) { |
| CHECK(root_container_ != nullptr); |
| } |
| |
| DexFileLoader(const uint8_t* base, size_t size, const std::string& location); |
| |
| DexFileLoader(std::vector<uint8_t>&& memory, const std::string& location); |
| |
| DexFileLoader(MemMap&& mem_map, const std::string& location); |
| |
| DexFileLoader(File* file, const std::string& location) |
| : DexFileLoader(/*filename=*/location.c_str(), file, location) {} |
| |
| DexFileLoader(const char* filename, const std::string& location) |
| : DexFileLoader(filename, /*file=*/&kInvalidFile, location) {} |
| |
| explicit DexFileLoader(const std::string& location) |
| : DexFileLoader(location.c_str(), /*file=*/&kInvalidFile, location) {} |
| |
| virtual ~DexFileLoader() {} |
| |
| // Open singe dex file at the given offset within the container (usually 0). |
| // This intentionally ignores all other dex files in the container |
| std::unique_ptr<const DexFile> OpenOne(size_t header_offset, |
| uint32_t location_checksum, |
| const OatDexFile* oat_dex_file, |
| bool verify, |
| bool verify_checksum, |
| std::string* error_msg); |
| |
| // Open single dex file (starting at offset 0 of the container). |
| // It expects only single dex file to be present and will fail otherwise. |
| std::unique_ptr<const DexFile> Open(uint32_t location_checksum, |
| const OatDexFile* oat_dex_file, |
| bool verify, |
| bool verify_checksum, |
| std::string* error_msg) { |
| std::unique_ptr<const DexFile> dex_file = OpenOne( |
| /*header_offset=*/0, location_checksum, oat_dex_file, verify, verify_checksum, error_msg); |
| // This API returns only singe DEX file, so check there is just single dex in the container. |
| CHECK(dex_file == nullptr || dex_file->IsDexContainerLastEntry()) << location_; |
| return dex_file; |
| } |
| |
| std::unique_ptr<const DexFile> Open(uint32_t location_checksum, |
| bool verify, |
| bool verify_checksum, |
| std::string* error_msg) { |
| return Open(location_checksum, |
| /*oat_dex_file=*/nullptr, |
| verify, |
| verify_checksum, |
| error_msg); |
| } |
| |
| // Opens all dex files, guessing the container format based on file magic. |
| bool Open(bool verify, |
| bool verify_checksum, |
| bool allow_no_dex_files, |
| DexFileLoaderErrorCode* error_code, |
| std::string* error_msg, |
| std::vector<std::unique_ptr<const DexFile>>* dex_files); |
| |
| bool Open(bool verify, |
| bool verify_checksum, |
| DexFileLoaderErrorCode* error_code, |
| std::string* error_msg, |
| std::vector<std::unique_ptr<const DexFile>>* dex_files) { |
| return Open(verify, |
| verify_checksum, |
| /*allow_no_dex_files=*/false, |
| error_code, |
| error_msg, |
| dex_files); |
| } |
| |
| bool Open(bool verify, |
| bool verify_checksum, |
| bool allow_no_dex_files, |
| std::string* error_msg, |
| std::vector<std::unique_ptr<const DexFile>>* dex_files) { |
| DexFileLoaderErrorCode error_code; |
| return Open(verify, verify_checksum, allow_no_dex_files, &error_code, error_msg, dex_files); |
| } |
| |
| bool Open(bool verify, |
| bool verify_checksum, |
| std::string* error_msg, |
| std::vector<std::unique_ptr<const DexFile>>* dex_files) { |
| DexFileLoaderErrorCode error_code; |
| return Open(verify, |
| verify_checksum, |
| /*allow_no_dex_files=*/false, |
| &error_code, |
| error_msg, |
| dex_files); |
| } |
| |
| protected: |
| static const File kInvalidFile; // Used for "no file descriptor" (-1). |
| |
| bool InitAndReadMagic(size_t header_offset, uint32_t* magic, std::string* error_msg); |
| |
| // Ensure we have root container. If we are backed by a file, memory-map it. |
| // We can only do this for dex files since zip files might be too big to map. |
| bool MapRootContainer(std::string* error_msg); |
| |
| static std::unique_ptr<DexFile> OpenCommon(std::shared_ptr<DexFileContainer> container, |
| const uint8_t* base, |
| size_t size, |
| const std::string& location, |
| std::optional<uint32_t> location_checksum, |
| const OatDexFile* oat_dex_file, |
| bool verify, |
| bool verify_checksum, |
| std::string* error_msg, |
| DexFileLoaderErrorCode* error_code); |
| |
| // Old signature preserved for app-compat. |
| std::unique_ptr<const DexFile> Open(const uint8_t* base, |
| size_t size, |
| const std::string& location, |
| uint32_t location_checksum, |
| const OatDexFile* oat_dex_file, |
| bool verify, |
| bool verify_checksum, |
| std::string* error_msg, |
| std::unique_ptr<DexFileContainer> container) const; |
| |
| // Old signature preserved for app-compat. |
| enum VerifyResult {}; |
| static std::unique_ptr<DexFile> OpenCommon(const uint8_t* base, |
| size_t size, |
| const uint8_t* data_base, |
| size_t data_size, |
| const std::string& location, |
| uint32_t location_checksum, |
| const OatDexFile* oat_dex_file, |
| bool verify, |
| bool verify_checksum, |
| std::string* error_msg, |
| std::unique_ptr<DexFileContainer> container, |
| VerifyResult* verify_result); |
| |
| // Open .dex files from the entry_name in a zip archive. |
| bool OpenFromZipEntry(const ZipArchive& zip_archive, |
| const char* entry_name, |
| const std::string& location, |
| bool verify, |
| bool verify_checksum, |
| /*inout*/ size_t* multidex_count, |
| /*out*/ DexFileLoaderErrorCode* error_code, |
| /*out*/ std::string* error_msg, |
| /*out*/ std::vector<std::unique_ptr<const DexFile>>* dex_files) const; |
| |
| // The DexFileLoader can be backed either by file or by memory (i.e. DexFileContainer). |
| // We can not just mmap the file since APKs might be unreasonably large for 32-bit system. |
| std::string filename_; |
| const File* file_ = &kInvalidFile; |
| std::optional<File> owned_file_; // May be used as backing storage for 'file_'. |
| std::shared_ptr<DexFileContainer> root_container_; |
| const std::string location_; |
| }; |
| |
| } // namespace art |
| |
| #endif // ART_LIBDEXFILE_DEX_DEX_FILE_LOADER_H_ |