/* * 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 #include #include #include #include #include #include #include #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 the multidex location and the checksum for each dex file in a zip or a dex container. // // This uses the source path provided to DexFileLoader constructor. // // Returns false on error. bool GetMultiDexChecksums(/*out*/ std::vector>* checksums, /*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 the source path provided to DexFileLoader constructor. // // Returns false on error. Sets *checksum to nullopt for an empty set. bool GetMultiDexChecksum(/*out*/ std::optional* 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 // array|vector. static uint32_t GetMultiDexChecksum(const DexFilePtrVector& dex_files, /*inout*/ size_t* i) { CHECK_LT(*i, dex_files.size()) << "No dex files"; std::optional checksum; for (; *i < dex_files.size(); ++(*i)) { const auto* dex_file = &*dex_files[*i]; bool is_primary_dex = !IsMultiDexLocation(dex_file->GetLocation()); 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 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 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&& 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 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 Open(uint32_t location_checksum, const OatDexFile* oat_dex_file, bool verify, bool verify_checksum, std::string* error_msg) { std::unique_ptr 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 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>* dex_files); bool Open(bool verify, bool verify_checksum, DexFileLoaderErrorCode* error_code, std::string* error_msg, std::vector>* 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>* 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>* 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 OpenCommon(std::shared_ptr container, const uint8_t* base, size_t size, const std::string& location, std::optional 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 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 container) const; // Old signature preserved for app-compat. enum VerifyResult {}; static std::unique_ptr 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 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>* 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 owned_file_; // May be used as backing storage for 'file_'. std::shared_ptr root_container_; // The full absolute path to the dex file, if it was loaded from disk. // // Can also be a path to a multidex container (typically apk), followed by // kMultiDexSeparator and the file inside the container. // // On host this may not be an absolute path. // // On device libnativeloader uses this to determine the location of the java // package or shared library, which decides where to load native libraries // from. const std::string location_; }; } // namespace art #endif // ART_LIBDEXFILE_DEX_DEX_FILE_LOADER_H_