Refactor DexFileLoader
The first parameter of the Open methods specifies the data source,
that we intend to load the dex file(s) from. This creates large number
of overloads when multiplied by diversity of the other arguments.
Move the data source parameters to the constructor of DexFileLoader.
Specifically, the constructor just creates the right DexFileContainer,
and the rest of the loader is independent of the data source used.
This removes large amount of the overloads as well as large amount
of copy-pasted code. Majority of ArtDexFileLoader has been removed.
Bug: 266950186
Test: ./art/test.py -b --host --optimizing --64
Change-Id: I6580b49e65441eec93a7e0124be23bd8c859904a
diff --git a/libdexfile/dex/art_dex_file_loader.cc b/libdexfile/dex/art_dex_file_loader.cc
index 53a85bc..3a15e90 100644
--- a/libdexfile/dex/art_dex_file_loader.cc
+++ b/libdexfile/dex/art_dex_file_loader.cc
@@ -37,67 +37,14 @@
namespace art {
-namespace {
-
-class MemMapContainer : public DexFileContainer {
- public:
- explicit MemMapContainer(MemMap&& mem_map, bool direct_mmap = false)
- : mem_map_(std::move(mem_map)), direct_mmap_(direct_mmap) {}
- ~MemMapContainer() override { }
-
- int GetPermissions() const {
- if (!mem_map_.IsValid()) {
- return 0;
- } else {
- return mem_map_.GetProtect();
- }
- }
-
- bool IsReadOnly() const override { return GetPermissions() == PROT_READ; }
-
- bool EnableWrite() override {
- CHECK(IsReadOnly());
- if (!mem_map_.IsValid()) {
- return false;
- } else {
- return mem_map_.Protect(PROT_READ | PROT_WRITE);
- }
- }
-
- bool DisableWrite() override {
- CHECK(!IsReadOnly());
- if (!mem_map_.IsValid()) {
- return false;
- } else {
- return mem_map_.Protect(PROT_READ);
- }
- }
-
- const uint8_t* Begin() const override { return mem_map_.Begin(); }
-
- const uint8_t* End() const override { return mem_map_.End(); }
-
- bool IsDirectMmap() override { return direct_mmap_; }
-
- private:
- MemMap mem_map_;
- bool direct_mmap_;
- DISALLOW_COPY_AND_ASSIGN(MemMapContainer);
-};
-
-} // namespace
-
using android::base::StringPrintf;
-static constexpr OatDexFile* kNoOatDexFile = nullptr;
-
-
bool ArtDexFileLoader::GetMultiDexChecksums(const char* filename,
std::vector<uint32_t>* checksums,
std::vector<std::string>* dex_locations,
std::string* error_msg,
int zip_fd,
- bool* zip_file_only_contains_uncompressed_dex) const {
+ bool* zip_file_only_contains_uncompressed_dex) {
CHECK(checksums != nullptr);
uint32_t magic;
@@ -154,459 +101,21 @@
return true;
}
if (IsMagicValid(magic)) {
- std::unique_ptr<const DexFile> dex_file(OpenFile(fd.Release(),
- filename,
- /* verify= */ false,
- /* verify_checksum= */ false,
- /* mmap_shared= */ false,
- error_msg));
- if (dex_file == nullptr) {
+ ArtDexFileLoader loader(fd.Release(), filename);
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+ if (!loader.Open(/* verify= */ false,
+ /* verify_checksum= */ false,
+ error_msg,
+ &dex_files)) {
return false;
}
- checksums->push_back(dex_file->GetHeader().checksum_);
+ for (auto& dex_file : dex_files) {
+ checksums->push_back(dex_file->GetHeader().checksum_);
+ }
return true;
}
*error_msg = StringPrintf("Expected valid zip or dex file: '%s'", filename);
return false;
}
-std::unique_ptr<const DexFile> ArtDexFileLoader::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) const {
- ScopedTrace trace(std::string("Open dex file from RAM ") + location);
- auto container = std::make_unique<MemoryDexFileContainer>(base, base + size);
- return OpenCommon(location,
- location_checksum,
- oat_dex_file,
- verify,
- verify_checksum,
- error_msg,
- std::move(container),
- /*verify_result=*/nullptr);
-}
-
-std::unique_ptr<const DexFile> ArtDexFileLoader::Open(std::unique_ptr<DexFileContainer> container,
- const std::string& location,
- uint32_t location_checksum,
- const OatDexFile* oat_dex_file,
- bool verify,
- bool verify_checksum,
- std::string* error_msg) const {
- ScopedTrace trace(std::string("Open dex file from ") + location);
- return OpenCommon(location,
- location_checksum,
- oat_dex_file,
- verify,
- verify_checksum,
- error_msg,
- std::move(container),
- /*verify_result=*/nullptr);
-}
-
-std::unique_ptr<const DexFile> ArtDexFileLoader::Open(const std::string& location,
- uint32_t location_checksum,
- MemMap&& map,
- bool verify,
- bool verify_checksum,
- std::string* error_msg) const {
- ScopedTrace trace(std::string("Open dex file from mapped-memory ") + location);
- CHECK(map.IsValid());
-
- size_t size = map.Size();
- if (size < sizeof(DexFile::Header)) {
- *error_msg = StringPrintf(
- "DexFile: failed to open dex file '%s' that is too short to have a header",
- location.c_str());
- return nullptr;
- }
-
- std::unique_ptr<DexFile> dex_file = OpenCommon(location,
- location_checksum,
- kNoOatDexFile,
- verify,
- verify_checksum,
- error_msg,
- std::make_unique<MemMapContainer>(std::move(map)),
- /*verify_result=*/nullptr);
- // Opening CompactDex is only supported from vdex files.
- if (dex_file != nullptr && dex_file->IsCompactDexFile()) {
- *error_msg = StringPrintf("Opening CompactDex file '%s' is only supported from vdex files",
- location.c_str());
- return nullptr;
- }
- return dex_file;
-}
-
-bool ArtDexFileLoader::Open(const char* filename,
- const std::string& location,
- bool verify,
- bool verify_checksum,
- std::string* error_msg,
- std::vector<std::unique_ptr<const DexFile>>* dex_files) const {
- uint32_t magic;
- File fd = OpenAndReadMagic(filename, &magic, error_msg);
- if (fd.Fd() == -1) {
- DCHECK(!error_msg->empty());
- return false;
- }
- return OpenWithMagic(
- magic, fd.Release(), location, verify, verify_checksum, error_msg, dex_files);
-}
-
-bool ArtDexFileLoader::Open(int fd,
- const std::string& location,
- bool verify,
- bool verify_checksum,
- std::string* error_msg,
- std::vector<std::unique_ptr<const DexFile>>* dex_files) const {
- uint32_t magic;
- if (!ReadMagicAndReset(fd, &magic, error_msg)) {
- DCHECK(!error_msg->empty());
- return false;
- }
- return OpenWithMagic(magic, fd, location, verify, verify_checksum, error_msg, dex_files);
-}
-
-bool ArtDexFileLoader::Open(const char* filename,
- int fd,
- const std::string& location,
- bool verify,
- bool verify_checksum,
- std::string* error_msg,
- std::vector<std::unique_ptr<const DexFile>>* dex_files) const {
- return fd == -1
- ? Open(filename, location, verify, verify_checksum, error_msg, dex_files)
- : Open(fd, location, verify, verify_checksum, error_msg, dex_files);
-}
-
-bool ArtDexFileLoader::OpenWithMagic(uint32_t magic,
- int fd,
- const std::string& location,
- bool verify,
- bool verify_checksum,
- std::string* error_msg,
- std::vector<std::unique_ptr<const DexFile>>* dex_files) const {
- ScopedTrace trace(std::string("Open dex file ") + std::string(location));
- DCHECK(dex_files != nullptr) << "DexFile::Open: out-param is nullptr";
- if (IsZipMagic(magic)) {
- return OpenZip(
- fd, location, verify, verify_checksum, /*allow_no_dex_files=*/false, error_msg, dex_files);
- }
- if (IsMagicValid(magic)) {
- std::unique_ptr<const DexFile> dex_file(OpenFile(fd,
- location,
- verify,
- verify_checksum,
- /* mmap_shared= */ false,
- error_msg));
- if (dex_file.get() != nullptr) {
- dex_files->push_back(std::move(dex_file));
- return true;
- } else {
- return false;
- }
- }
- *error_msg = StringPrintf("Expected valid zip or dex file: '%s'", location.c_str());
- return false;
-}
-
-std::unique_ptr<const DexFile> ArtDexFileLoader::OpenDex(int fd,
- const std::string& location,
- bool verify,
- bool verify_checksum,
- bool mmap_shared,
- std::string* error_msg) const {
- ScopedTrace trace("Open dex file " + std::string(location));
- return OpenFile(fd, location, verify, verify_checksum, mmap_shared, error_msg);
-}
-
-bool ArtDexFileLoader::OpenZip(int fd,
- const std::string& location,
- bool verify,
- bool verify_checksum,
- bool allow_no_dex_files,
- std::string* error_msg,
- std::vector<std::unique_ptr<const DexFile>>* dex_files) const {
- ScopedTrace trace("Dex file open Zip " + std::string(location));
- return OpenZipInternal(ZipArchive::OpenFromFd(fd, location.c_str(), error_msg),
- location,
- verify,
- verify_checksum,
- allow_no_dex_files,
- error_msg,
- dex_files);
-}
-
-bool ArtDexFileLoader::OpenZipFromOwnedFd(
- int fd,
- const std::string& location,
- bool verify,
- bool verify_checksum,
- bool allow_no_dex_files,
- std::string* error_msg,
- std::vector<std::unique_ptr<const DexFile>>* dex_files) const {
- ScopedTrace trace("Dex file open Zip " + std::string(location) + " (owned fd)");
- return OpenZipInternal(ZipArchive::OpenFromOwnedFd(fd, location.c_str(), error_msg),
- location,
- verify,
- verify_checksum,
- allow_no_dex_files,
- error_msg,
- dex_files);
-}
-
-bool ArtDexFileLoader::OpenZipInternal(
- ZipArchive* raw_zip_archive,
- const std::string& location,
- bool verify,
- bool verify_checksum,
- bool allow_no_dex_files,
- std::string* error_msg,
- std::vector<std::unique_ptr<const DexFile>>* dex_files) const {
- DCHECK(dex_files != nullptr) << "DexFile::OpenZip: out-param is nullptr";
- std::unique_ptr<ZipArchive> zip_archive(raw_zip_archive);
- if (zip_archive.get() == nullptr) {
- DCHECK(!error_msg->empty());
- return false;
- }
- return OpenAllDexFilesFromZip(
- *zip_archive, location, verify, verify_checksum, allow_no_dex_files, error_msg, dex_files);
-}
-
-std::unique_ptr<const DexFile> ArtDexFileLoader::OpenFile(int fd,
- const std::string& location,
- bool verify,
- bool verify_checksum,
- bool mmap_shared,
- std::string* error_msg) const {
- ScopedTrace trace(std::string("Open dex file ") + std::string(location));
- CHECK(!location.empty());
- MemMap map;
- {
- File delayed_close(fd, /* check_usage= */ false);
- struct stat sbuf;
- memset(&sbuf, 0, sizeof(sbuf));
- if (fstat(fd, &sbuf) == -1) {
- *error_msg = StringPrintf("DexFile: fstat '%s' failed: %s", location.c_str(),
- strerror(errno));
- return nullptr;
- }
- if (S_ISDIR(sbuf.st_mode)) {
- *error_msg = StringPrintf("Attempt to mmap directory '%s'", location.c_str());
- return nullptr;
- }
- size_t length = sbuf.st_size;
- map = MemMap::MapFile(length,
- PROT_READ,
- mmap_shared ? MAP_SHARED : MAP_PRIVATE,
- fd,
- 0,
- /*low_4gb=*/false,
- location.c_str(),
- error_msg);
- if (!map.IsValid()) {
- DCHECK(!error_msg->empty());
- return nullptr;
- }
- }
-
- const uint8_t* begin = map.Begin();
- size_t size = map.Size();
- if (size < sizeof(DexFile::Header)) {
- *error_msg = StringPrintf(
- "DexFile: failed to open dex file '%s' that is too short to have a header",
- location.c_str());
- return nullptr;
- }
-
- const DexFile::Header* dex_header = reinterpret_cast<const DexFile::Header*>(begin);
-
- std::unique_ptr<DexFile> dex_file = OpenCommon(location,
- dex_header->checksum_,
- kNoOatDexFile,
- verify,
- verify_checksum,
- error_msg,
- std::make_unique<MemMapContainer>(std::move(map)),
- /*verify_result=*/nullptr);
-
- // Opening CompactDex is only supported from vdex files.
- if (dex_file != nullptr && dex_file->IsCompactDexFile()) {
- *error_msg = StringPrintf("Opening CompactDex file '%s' is only supported from vdex files",
- location.c_str());
- return nullptr;
- }
- return dex_file;
-}
-
-std::unique_ptr<const DexFile> ArtDexFileLoader::OpenOneDexFileFromZip(
- const ZipArchive& zip_archive,
- const char* entry_name,
- const std::string& location,
- bool verify,
- bool verify_checksum,
- std::string* error_msg,
- DexFileLoaderErrorCode* error_code) const {
- ScopedTrace trace("Dex file open from Zip Archive " + std::string(location));
- CHECK(!location.empty());
- std::unique_ptr<ZipEntry> zip_entry(zip_archive.Find(entry_name, error_msg));
- if (zip_entry == nullptr) {
- *error_code = DexFileLoaderErrorCode::kEntryNotFound;
- return nullptr;
- }
- if (zip_entry->GetUncompressedLength() == 0) {
- *error_msg = StringPrintf("Dex file '%s' has zero length", location.c_str());
- *error_code = DexFileLoaderErrorCode::kDexFileError;
- return nullptr;
- }
-
- MemMap map;
- bool direct_mmap = false;
- if (zip_entry->IsUncompressed()) {
- if (!zip_entry->IsAlignedTo(alignof(DexFile::Header))) {
- // Do not mmap unaligned ZIP entries because
- // doing so would fail dex verification which requires 4 byte alignment.
- LOG(WARNING) << "Can't mmap dex file " << location << "!" << entry_name << " directly; "
- << "please zipalign to " << alignof(DexFile::Header) << " bytes. "
- << "Falling back to extracting file.";
- } else {
- // Map uncompressed files within zip as file-backed to avoid a dirty copy.
- map = zip_entry->MapDirectlyFromFile(location.c_str(), /*out*/error_msg);
- if (!map.IsValid()) {
- LOG(WARNING) << "Can't mmap dex file " << location << "!" << entry_name << " directly; "
- << "is your ZIP file corrupted? Falling back to extraction.";
- // Try again with Extraction which still has a chance of recovery.
- }
- direct_mmap = true;
- }
- }
-
- ScopedTrace map_extract_trace(StringPrintf("Mapped=%s Extracted=%s",
- map.IsValid() ? "true" : "false",
- map.IsValid() ? "false" : "true")); // this is redundant but much easier to read in traces.
-
- if (!map.IsValid()) {
- // Default path for compressed ZIP entries,
- // and fallback for stored ZIP entries.
- map = zip_entry->ExtractToMemMap(location.c_str(), entry_name, error_msg);
- }
-
- if (!map.IsValid()) {
- *error_msg = StringPrintf("Failed to extract '%s' from '%s': %s", entry_name, location.c_str(),
- error_msg->c_str());
- *error_code = DexFileLoaderErrorCode::kExtractToMemoryError;
- return nullptr;
- }
- VerifyResult verify_result;
- auto container = std::make_unique<MemMapContainer>(std::move(map), direct_mmap);
- std::unique_ptr<DexFile> dex_file = OpenCommon(location,
- zip_entry->GetCrc32(),
- kNoOatDexFile,
- verify,
- verify_checksum,
- error_msg,
- std::move(container),
- &verify_result);
- if (dex_file != nullptr && dex_file->IsCompactDexFile()) {
- *error_msg = StringPrintf("Opening CompactDex file '%s' is only supported from vdex files",
- location.c_str());
- return nullptr;
- }
- if (dex_file == nullptr) {
- if (verify_result == VerifyResult::kVerifyNotAttempted) {
- *error_code = DexFileLoaderErrorCode::kDexFileError;
- } else {
- *error_code = DexFileLoaderErrorCode::kVerifyError;
- }
- return nullptr;
- }
- if (!dex_file->DisableWrite()) {
- *error_msg = StringPrintf("Failed to make dex file '%s' read only", location.c_str());
- *error_code = DexFileLoaderErrorCode::kMakeReadOnlyError;
- return nullptr;
- }
- CHECK(dex_file->IsReadOnly()) << location;
- if (verify_result != VerifyResult::kVerifySucceeded) {
- *error_code = DexFileLoaderErrorCode::kVerifyError;
- return nullptr;
- }
- *error_code = DexFileLoaderErrorCode::kNoError;
- return dex_file;
-}
-
-// Technically we do not have a limitation with respect to the number of dex files that can be in a
-// multidex APK. However, it's bad practice, as each dex file requires its own tables for symbols
-// (types, classes, methods, ...) and dex caches. So warn the user that we open a zip with what
-// seems an excessive number.
-static constexpr size_t kWarnOnManyDexFilesThreshold = 100;
-
-bool ArtDexFileLoader::OpenAllDexFilesFromZip(
- const ZipArchive& zip_archive,
- const std::string& location,
- bool verify,
- bool verify_checksum,
- bool allow_no_dex_files,
- std::string* error_msg,
- std::vector<std::unique_ptr<const DexFile>>* dex_files) const {
- ScopedTrace trace("Dex file open from Zip " + std::string(location));
- DCHECK(dex_files != nullptr) << "DexFile::OpenFromZip: out-param is nullptr";
- DexFileLoaderErrorCode error_code;
- std::string local_error_msg;
- std::unique_ptr<const DexFile> dex_file(OpenOneDexFileFromZip(
- zip_archive, kClassesDex, location, verify, verify_checksum, &local_error_msg, &error_code));
- if (dex_file.get() == nullptr) {
- if (allow_no_dex_files && error_code == DexFileLoaderErrorCode::kEntryNotFound) {
- return true;
- }
- *error_msg = std::move(local_error_msg);
- return false;
- } else {
- // Had at least classes.dex.
- dex_files->push_back(std::move(dex_file));
-
- // Now try some more.
-
- // We could try to avoid std::string allocations by working on a char array directly. As we
- // do not expect a lot of iterations, this seems too involved and brittle.
-
- for (size_t i = 1;; ++i) {
- std::string name = GetMultiDexClassesDexName(i);
- std::string fake_location = GetMultiDexLocation(i, location.c_str());
- std::unique_ptr<const DexFile> next_dex_file(OpenOneDexFileFromZip(zip_archive,
- name.c_str(),
- fake_location,
- verify,
- verify_checksum,
- error_msg,
- &error_code));
- if (next_dex_file.get() == nullptr) {
- if (error_code != DexFileLoaderErrorCode::kEntryNotFound) {
- LOG(WARNING) << "Zip open failed: " << *error_msg;
- }
- break;
- } else {
- dex_files->push_back(std::move(next_dex_file));
- }
-
- if (i == kWarnOnManyDexFilesThreshold) {
- LOG(WARNING) << location << " has in excess of " << kWarnOnManyDexFilesThreshold
- << " dex files. Please consider coalescing and shrinking the number to "
- " avoid runtime overhead.";
- }
-
- if (i == std::numeric_limits<size_t>::max()) {
- LOG(ERROR) << "Overflow in number of dex files!";
- break;
- }
- }
-
- return true;
- }
-}
-
} // namespace art
diff --git a/libdexfile/dex/art_dex_file_loader.h b/libdexfile/dex/art_dex_file_loader.h
index 6f8b3c6..9806c46 100644
--- a/libdexfile/dex/art_dex_file_loader.h
+++ b/libdexfile/dex/art_dex_file_loader.h
@@ -36,6 +36,7 @@
// Class that is used to open dex files and deal with corresponding multidex and location logic.
class ArtDexFileLoader : public DexFileLoader {
public:
+ using DexFileLoader::DexFileLoader;
virtual ~ArtDexFileLoader() { }
// Returns the checksums of a file for comparison with GetLocationChecksum().
@@ -51,140 +52,12 @@
// locations.
//
// Return true if the checksums could be found, false otherwise.
- bool GetMultiDexChecksums(const char* filename,
- std::vector<uint32_t>* checksums,
- std::vector<std::string>* dex_locations,
- std::string* error_msg,
- int zip_fd = -1,
- bool* only_contains_uncompressed_dex = nullptr) const override;
-
- // Opens .dex file, backed by existing memory
- 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) const override;
-
- // Opens .dex file, backed by existing memory
- std::unique_ptr<const DexFile> Open(std::unique_ptr<DexFileContainer> container,
- const std::string& location,
- uint32_t location_checksum,
- const OatDexFile* oat_dex_file,
- bool verify,
- bool verify_checksum,
- std::string* error_msg) const;
-
- // Opens .dex file that has been memory-mapped by the caller.
- std::unique_ptr<const DexFile> Open(const std::string& location,
- uint32_t location_checkum,
- MemMap&& mem_map,
- bool verify,
- bool verify_checksum,
- std::string* error_msg) const;
-
- // Opens all .dex files found in the file, guessing the container format based on file magic.
- bool Open(const char* filename,
- const std::string& location,
- bool verify,
- bool verify_checksum,
- std::string* error_msg,
- std::vector<std::unique_ptr<const DexFile>>* dex_files) const;
- bool Open(int fd,
- const std::string& location,
- bool verify,
- bool verify_checksum,
- std::string* error_msg,
- std::vector<std::unique_ptr<const DexFile>>* dex_files) const;
- // Opens all .dex files found in the file, guessing the container format based on file magic.
- // If the fd is -1 then the dex files are opened using the filename; otherwise they are
- // opened using the fd.
- bool Open(const char* filename,
- int fd,
- const std::string& location,
- bool verify,
- bool verify_checksum,
- std::string* error_msg,
- std::vector<std::unique_ptr<const DexFile>>* dex_files) const;
-
- // Open a single dex file from an fd. This function closes the fd.
- std::unique_ptr<const DexFile> OpenDex(int fd,
- const std::string& location,
- bool verify,
- bool verify_checksum,
- bool mmap_shared,
- std::string* error_msg) const;
-
- // Opens dex files from within a .jar, .zip, or .apk file using its file descriptor. The file
- // descriptor ownership is taken over, i.e. will be closed by this class.
- // If the zip file doesn't contain any dex code and `allow_no_dex_files` is true, returns true and
- // keeps `dex_files` to be an empty vector; if the zip file doesn't contain any dex code and
- // `allow_no_dex_files` is false, returns false and sets the error message.
- bool OpenZip(int fd,
- const std::string& location,
- bool verify,
- bool verify_checksum,
- bool allow_no_dex_files,
- std::string* error_msg,
- std::vector<std::unique_ptr<const DexFile>>* dex_files) const;
-
- // Opens dex files from within a .jar, .zip, or .apk file using its file descriptor. The file
- // descriptor is assumed owned by the caller.
- // If the zip file doesn't contain any dex code and `allow_no_dex_files` is true, returns true and
- // keeps `dex_files` to be an empty vector; if the zip file doesn't contain any dex code and
- // `allow_no_dex_files` is false, returns false and sets the error message.
- bool OpenZipFromOwnedFd(int fd,
- const std::string& location,
- bool verify,
- bool verify_checksum,
- bool allow_no_dex_files,
- std::string* error_msg,
- std::vector<std::unique_ptr<const DexFile>>* dex_files) const;
-
- private:
- bool OpenWithMagic(uint32_t magic,
- int fd,
- const std::string& location,
- bool verify,
- bool verify_checksum,
- std::string* error_msg,
- std::vector<std::unique_ptr<const DexFile>>* dex_files) const;
-
- std::unique_ptr<const DexFile> OpenFile(int fd,
- const std::string& location,
- bool verify,
- bool verify_checksum,
- bool mmap_shared,
- std::string* error_msg) const;
-
- // Open all classesXXX.dex files from a zip archive.
- bool OpenAllDexFilesFromZip(const ZipArchive& zip_archive,
- const std::string& location,
- bool verify,
- bool verify_checksum,
- bool allow_no_dex_files,
- std::string* error_msg,
- std::vector<std::unique_ptr<const DexFile>>* dex_files) const;
-
- // Opens .dex file from the entry_name in a zip archive. error_code is undefined when non-null
- // return.
- std::unique_ptr<const DexFile> OpenOneDexFileFromZip(const ZipArchive& zip_archive,
- const char* entry_name,
- const std::string& location,
- bool verify,
- bool verify_checksum,
- std::string* error_msg,
- DexFileLoaderErrorCode* error_code) const;
-
- bool OpenZipInternal(ZipArchive* raw_zip_archive,
- const std::string& location,
- bool verify,
- bool verify_checksum,
- bool allow_no_dex_files,
- std::string* error_msg,
- std::vector<std::unique_ptr<const DexFile>>* dex_files) const;
+ static bool GetMultiDexChecksums(const char* filename,
+ std::vector<uint32_t>* checksums,
+ std::vector<std::string>* dex_locations,
+ std::string* error_msg,
+ int zip_fd = -1,
+ bool* only_contains_uncompressed_dex = nullptr);
};
} // namespace art
diff --git a/libdexfile/dex/art_dex_file_loader_test.cc b/libdexfile/dex/art_dex_file_loader_test.cc
index 477d8c5..833e73b 100644
--- a/libdexfile/dex/art_dex_file_loader_test.cc
+++ b/libdexfile/dex/art_dex_file_loader_test.cc
@@ -61,16 +61,14 @@
std::string zip_file = GetTestDexFileName("MultiDex");
File file(zip_file, O_RDONLY, /*check_usage=*/false);
ASSERT_GE(file.Fd(), 0);
- ArtDexFileLoader dex_file_loader;
std::vector<std::unique_ptr<const DexFile>> dex_files;
std::string error_msg;
- ASSERT_TRUE(dex_file_loader.OpenZip(file.Release(),
- zip_file,
- /*verify=*/false,
- /*verify_checksum=*/true,
- /*allow_no_dex_files=*/true,
- &error_msg,
- &dex_files))
+ ArtDexFileLoader dex_file_loader(file.Release(), zip_file);
+ ASSERT_TRUE(dex_file_loader.Open(/*verify=*/false,
+ /*verify_checksum=*/true,
+ /*allow_no_dex_files=*/true,
+ &error_msg,
+ &dex_files))
<< error_msg;
EXPECT_GT(dex_files.size(), 1);
}
@@ -79,16 +77,14 @@
std::string zip_file = GetTestDexFileName("MainEmptyUncompressed");
File file(zip_file, O_RDONLY, /*check_usage=*/false);
ASSERT_GE(file.Fd(), 0);
- ArtDexFileLoader dex_file_loader;
std::vector<std::unique_ptr<const DexFile>> dex_files;
std::string error_msg;
- ASSERT_TRUE(dex_file_loader.OpenZip(file.Release(),
- zip_file,
- /*verify=*/false,
- /*verify_checksum=*/true,
- /*allow_no_dex_files=*/true,
- &error_msg,
- &dex_files))
+ ArtDexFileLoader dex_file_loader(file.Release(), zip_file);
+ ASSERT_TRUE(dex_file_loader.Open(/*verify=*/false,
+ /*verify_checksum=*/true,
+ /*allow_no_dex_files=*/true,
+ &error_msg,
+ &dex_files))
<< error_msg;
EXPECT_EQ(dex_files.size(), 0);
}
@@ -102,11 +98,8 @@
std::vector<uint32_t> checksums;
std::vector<std::string> dex_locations;
std::string error_msg;
- const ArtDexFileLoader dex_file_loader;
- EXPECT_TRUE(dex_file_loader.GetMultiDexChecksums(GetLibCoreDexFileNames()[0].c_str(),
- &checksums,
- &dex_locations,
- &error_msg))
+ EXPECT_TRUE(ArtDexFileLoader::GetMultiDexChecksums(
+ GetLibCoreDexFileNames()[0].c_str(), &checksums, &dex_locations, &error_msg))
<< error_msg;
ASSERT_EQ(1U, checksums.size());
ASSERT_EQ(1U, dex_locations.size());
@@ -119,11 +112,9 @@
std::vector<uint32_t> checksums;
std::vector<std::string> dex_locations;
std::string multidex_file = GetTestDexFileName("MultiDex");
- const ArtDexFileLoader dex_file_loader;
- EXPECT_TRUE(dex_file_loader.GetMultiDexChecksums(multidex_file.c_str(),
- &checksums,
- &dex_locations,
- &error_msg)) << error_msg;
+ EXPECT_TRUE(ArtDexFileLoader::GetMultiDexChecksums(
+ multidex_file.c_str(), &checksums, &dex_locations, &error_msg))
+ << error_msg;
std::vector<std::unique_ptr<const DexFile>> dexes = OpenTestDexFiles("MultiDex");
ASSERT_EQ(2U, dexes.size());
@@ -144,8 +135,7 @@
std::vector<uint32_t> checksums;
std::vector<std::string> dex_locations;
std::string multidex_file = GetTestDexFileName("MainEmptyUncompressed");
- const ArtDexFileLoader dex_file_loader;
- EXPECT_TRUE(dex_file_loader.GetMultiDexChecksums(
+ EXPECT_TRUE(ArtDexFileLoader::GetMultiDexChecksums(
multidex_file.c_str(), &checksums, &dex_locations, &error_msg))
<< error_msg;
diff --git a/libdexfile/dex/code_item_accessors_test.cc b/libdexfile/dex/code_item_accessors_test.cc
index c5891f9..d43db70 100644
--- a/libdexfile/dex/code_item_accessors_test.cc
+++ b/libdexfile/dex/code_item_accessors_test.cc
@@ -40,12 +40,9 @@
StandardDexFile::WriteMagic(data->data());
StandardDexFile::WriteCurrentVersion(data->data());
}
- const DexFileLoader dex_file_loader;
+ DexFileLoader dex_file_loader(data->data(), data->size(), "location");
std::string error_msg;
- std::unique_ptr<const DexFile> dex(dex_file_loader.Open(data->data(),
- data->size(),
- "location",
- /*location_checksum=*/ 123,
+ std::unique_ptr<const DexFile> dex(dex_file_loader.Open(/*location_checksum=*/123,
/*oat_dex_file=*/nullptr,
/*verify=*/false,
/*verify_checksum=*/false,
diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h
index c9a16ae..0cf5ae2 100644
--- a/libdexfile/dex/dex_file.h
+++ b/libdexfile/dex/dex_file.h
@@ -58,9 +58,14 @@
public:
DexFileContainer() { }
virtual ~DexFileContainer() {}
+
virtual bool IsReadOnly() const = 0;
+
+ // Make the underlying writeable. Return true on success (memory can be written).
virtual bool EnableWrite() = 0;
+ // Make the underlying read-only. Return true on success (memory is read-only now).
virtual bool DisableWrite() = 0;
+
virtual const uint8_t* Begin() const = 0;
virtual const uint8_t* End() const = 0;
size_t Size() const { return End() - Begin(); }
@@ -70,9 +75,12 @@
virtual const uint8_t* DataBegin() const { return nullptr; }
virtual const uint8_t* DataEnd() const { return nullptr; }
- virtual bool IsDirectMmap() { return false; }
+ bool IsZip() const { return is_zip_; }
+ void SetIsZip() { is_zip_ = true; }
+ virtual bool IsFileMap() const { return false; }
private:
+ bool is_zip_ = false;
DISALLOW_COPY_AND_ASSIGN(DexFileContainer);
};
diff --git a/libdexfile/dex/dex_file_loader.cc b/libdexfile/dex/dex_file_loader.cc
index 541d10b..942a4bc 100644
--- a/libdexfile/dex/dex_file_loader.cc
+++ b/libdexfile/dex/dex_file_loader.cc
@@ -16,20 +16,30 @@
#include "dex_file_loader.h"
+#include <sys/stat.h>
+
#include <memory>
#include "android-base/stringprintf.h"
+#include "base/bit_utils.h"
+#include "base/file_magic.h"
+#include "base/mem_map.h"
+#include "base/os.h"
#include "base/stl_util.h"
+#include "base/systrace.h"
+#include "base/unix_file/fd_file.h"
+#include "base/zip_archive.h"
#include "compact_dex_file.h"
#include "dex_file.h"
#include "dex_file_verifier.h"
#include "standard_dex_file.h"
-#include "ziparchive/zip_archive.h"
namespace art {
namespace {
+using android::base::StringPrintf;
+
class VectorContainer : public DexFileContainer {
public:
explicit VectorContainer(std::vector<uint8_t>&& vector) : vector_(std::move(vector)) { }
@@ -37,13 +47,9 @@
bool IsReadOnly() const override { return true; }
- bool EnableWrite() override {
- return false;
- }
+ bool EnableWrite() override { return true; }
- bool DisableWrite() override {
- return false;
- }
+ bool DisableWrite() override { return false; }
const uint8_t* Begin() const override { return vector_.data(); }
@@ -54,100 +60,53 @@
DISALLOW_COPY_AND_ASSIGN(VectorContainer);
};
+class MemMapContainer : public DexFileContainer {
+ public:
+ explicit MemMapContainer(MemMap&& mem_map, bool is_file_map = false)
+ : mem_map_(std::move(mem_map)), is_file_map_(is_file_map) {}
+
+ int GetPermissions() const {
+ if (!mem_map_.IsValid()) {
+ return 0;
+ } else {
+ return mem_map_.GetProtect();
+ }
+ }
+
+ bool IsReadOnly() const override { return GetPermissions() == PROT_READ; }
+
+ bool EnableWrite() override {
+ CHECK(IsReadOnly());
+ if (!mem_map_.IsValid()) {
+ return false;
+ } else {
+ return mem_map_.Protect(PROT_READ | PROT_WRITE);
+ }
+ }
+
+ bool DisableWrite() override {
+ CHECK(!IsReadOnly());
+ if (!mem_map_.IsValid()) {
+ return false;
+ } else {
+ return mem_map_.Protect(PROT_READ);
+ }
+ }
+
+ const uint8_t* Begin() const override { return mem_map_.Begin(); }
+
+ const uint8_t* End() const override { return mem_map_.End(); }
+
+ bool IsFileMap() const override { return is_file_map_; }
+
+ protected:
+ MemMap mem_map_;
+ bool is_file_map_;
+ DISALLOW_COPY_AND_ASSIGN(MemMapContainer);
+};
+
} // namespace
-using android::base::StringPrintf;
-
-class DexZipArchive;
-
-class DexZipEntry {
- public:
- // Extract this entry to memory.
- // Returns null on failure and sets error_msg.
- const std::vector<uint8_t> Extract(std::string* error_msg) {
- std::vector<uint8_t> map(GetUncompressedLength());
- if (map.size() == 0) {
- DCHECK(!error_msg->empty());
- return map;
- }
- const int32_t error = ExtractToMemory(handle_, zip_entry_, map.data(), map.size());
- if (error) {
- *error_msg = std::string(ErrorCodeString(error));
- }
- return map;
- }
-
- virtual ~DexZipEntry() {
- delete zip_entry_;
- }
-
- uint32_t GetUncompressedLength() {
- return zip_entry_->uncompressed_length;
- }
-
- uint32_t GetCrc32() {
- return zip_entry_->crc32;
- }
-
- private:
- DexZipEntry(ZipArchiveHandle handle,
- ::ZipEntry* zip_entry,
- const std::string& entry_name)
- : handle_(handle), zip_entry_(zip_entry), entry_name_(entry_name) {}
-
- ZipArchiveHandle handle_;
- ::ZipEntry* const zip_entry_;
- std::string const entry_name_;
-
- friend class DexZipArchive;
- DISALLOW_COPY_AND_ASSIGN(DexZipEntry);
-};
-
-class DexZipArchive {
- public:
- // return new DexZipArchive instance on success, null on error.
- static DexZipArchive* Open(const uint8_t* base, size_t size, std::string* error_msg) {
- ZipArchiveHandle handle;
- uint8_t* nonconst_base = const_cast<uint8_t*>(base);
- const int32_t error = OpenArchiveFromMemory(nonconst_base, size, "ZipArchiveMemory", &handle);
- if (error) {
- *error_msg = std::string(ErrorCodeString(error));
- CloseArchive(handle);
- return nullptr;
- }
- return new DexZipArchive(handle);
- }
-
- DexZipEntry* Find(const char* name, std::string* error_msg) const {
- DCHECK(name != nullptr);
- // Resist the urge to delete the space. <: is a bigraph sequence.
- std::unique_ptr< ::ZipEntry> zip_entry(new ::ZipEntry);
- const int32_t error = FindEntry(handle_, name, zip_entry.get());
- if (error) {
- *error_msg = std::string(ErrorCodeString(error));
- return nullptr;
- }
- return new DexZipEntry(handle_, zip_entry.release(), name);
- }
-
- ~DexZipArchive() {
- CloseArchive(handle_);
- }
-
-
- private:
- explicit DexZipArchive(ZipArchiveHandle handle) : handle_(handle) {}
- ZipArchiveHandle handle_;
-
- friend class DexZipEntry;
- DISALLOW_COPY_AND_ASSIGN(DexZipArchive);
-};
-
-static bool IsZipMagic(uint32_t magic) {
- return (('P' == ((magic >> 0) & 0xff)) &&
- ('K' == ((magic >> 8) & 0xff)));
-}
-
bool DexFileLoader::IsMagicValid(uint32_t magic) {
return IsMagicValid(reinterpret_cast<uint8_t*>(&magic));
}
@@ -205,91 +164,152 @@
}
// All of the implementations here should be independent of the runtime.
-// TODO: implement all the virtual methods.
-bool DexFileLoader::GetMultiDexChecksums(
- const char* filename ATTRIBUTE_UNUSED,
- std::vector<uint32_t>* checksums ATTRIBUTE_UNUSED,
- std::vector<std::string>* dex_locations ATTRIBUTE_UNUSED,
- std::string* error_msg,
- int zip_fd ATTRIBUTE_UNUSED,
- bool* zip_file_only_contains_uncompress_dex ATTRIBUTE_UNUSED) const {
- *error_msg = "UNIMPLEMENTED";
- return false;
-}
+DexFileLoader::DexFileLoader(const uint8_t* base, size_t size, const std::string& location)
+ : DexFileLoader(std::make_unique<MemoryDexFileContainer>(base, base + size), location) {}
-std::unique_ptr<const DexFile> DexFileLoader::Open(
- const std::string& location,
- uint32_t location_checksum,
- std::vector<uint8_t>&& memory,
- const OatDexFile* oat_dex_file,
- bool verify,
- bool verify_checksum,
- std::string* error_msg) {
- return OpenCommon(location,
- location_checksum,
- oat_dex_file,
- verify,
- verify_checksum,
- error_msg,
- std::make_unique<VectorContainer>(std::move(memory)),
- /*verify_result=*/nullptr);
-}
+DexFileLoader::DexFileLoader(std::vector<uint8_t>&& memory, const std::string& location)
+ : DexFileLoader(std::make_unique<VectorContainer>(std::move(memory)), location) {}
-std::unique_ptr<const DexFile> DexFileLoader::Open(const uint8_t* base,
- size_t size,
- const std::string& location,
- uint32_t location_checksum,
+DexFileLoader::DexFileLoader(MemMap&& mem_map, const std::string& location)
+ : DexFileLoader(std::make_unique<MemMapContainer>(std::move(mem_map)), location) {}
+
+std::unique_ptr<const DexFile> DexFileLoader::Open(uint32_t location_checksum,
const OatDexFile* oat_dex_file,
bool verify,
bool verify_checksum,
- std::string* error_msg) const {
- auto container = std::make_unique<MemoryDexFileContainer>(base, base + size);
- return OpenCommon(location,
- location_checksum,
- oat_dex_file,
- verify,
- verify_checksum,
- error_msg,
- std::move(container),
- /*verify_result=*/nullptr);
+ std::string* error_msg) {
+ ScopedTrace trace(std::string("Open dex file ") + location_);
+
+ uint32_t magic;
+ if (!InitAndReadMagic(&magic, error_msg) || !MapRootContainer(error_msg)) {
+ DCHECK(!error_msg->empty());
+ return {};
+ }
+ std::unique_ptr<const DexFile> dex_file = OpenCommon(std::move(root_container_),
+ location_,
+ location_checksum,
+ oat_dex_file,
+ verify,
+ verify_checksum,
+ error_msg,
+ nullptr);
+ return dex_file;
}
-bool DexFileLoader::OpenAll(
- const uint8_t* base,
- size_t size,
- const std::string& location,
- bool verify,
- bool verify_checksum,
- DexFileLoaderErrorCode* error_code,
- std::string* error_msg,
- std::vector<std::unique_ptr<const DexFile>>* dex_files) const {
+bool DexFileLoader::InitAndReadMagic(uint32_t* magic, std::string* error_msg) {
+ if (root_container_ != nullptr) {
+ if (root_container_->Size() < sizeof(uint32_t)) {
+ *error_msg = StringPrintf("Unable to open '%s' : Size is too small", location_.c_str());
+ return false;
+ }
+ *magic = *reinterpret_cast<const uint32_t*>(root_container_->Begin());
+ } else {
+ // Open the file if we have not been given the file-descriptor directly before.
+ if (!file_.has_value()) {
+ CHECK(!filename_.empty());
+ file_.emplace(filename_, O_RDONLY, /* check_usage= */ false);
+ if (file_->Fd() == -1) {
+ *error_msg = StringPrintf("Unable to open '%s' : %s", filename_.c_str(), strerror(errno));
+ return false;
+ }
+ }
+ if (!ReadMagicAndReset(file_->Fd(), magic, error_msg)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool DexFileLoader::MapRootContainer(std::string* error_msg) {
+ if (root_container_ != nullptr) {
+ return true;
+ }
+
+ CHECK(MemMap::IsInitialized());
+ CHECK(file_.has_value());
+ struct stat sbuf;
+ memset(&sbuf, 0, sizeof(sbuf));
+ if (fstat(file_->Fd(), &sbuf) == -1) {
+ *error_msg = StringPrintf("DexFile: fstat '%s' failed: %s", filename_.c_str(), strerror(errno));
+ return false;
+ }
+ if (S_ISDIR(sbuf.st_mode)) {
+ *error_msg = StringPrintf("Attempt to mmap directory '%s'", filename_.c_str());
+ return false;
+ }
+ MemMap map = MemMap::MapFile(sbuf.st_size,
+ PROT_READ,
+ MAP_PRIVATE,
+ file_->Fd(),
+ 0,
+ /*low_4gb=*/false,
+ filename_.c_str(),
+ error_msg);
+ if (!map.IsValid()) {
+ DCHECK(!error_msg->empty());
+ return false;
+ }
+ root_container_ = std::make_unique<MemMapContainer>(std::move(map));
+ return true;
+}
+
+bool DexFileLoader::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) {
+ ScopedTrace trace(std::string("Open dex file ") + location_);
+
DCHECK(dex_files != nullptr) << "DexFile::Open: out-param is nullptr";
- uint32_t magic = *reinterpret_cast<const uint32_t*>(base);
+
+ uint32_t magic;
+ if (!InitAndReadMagic(&magic, error_msg)) {
+ return false;
+ }
+
if (IsZipMagic(magic)) {
- std::unique_ptr<DexZipArchive> zip_archive(DexZipArchive::Open(base, size, error_msg));
+ std::unique_ptr<ZipArchive> zip_archive(
+ file_.has_value() ?
+ ZipArchive::OpenFromOwnedFd(file_->Fd(), location_.c_str(), error_msg) :
+ ZipArchive::OpenFromMemory(
+ root_container_->Begin(), root_container_->Size(), location_.c_str(), error_msg));
if (zip_archive.get() == nullptr) {
DCHECK(!error_msg->empty());
return false;
}
- return OpenAllDexFilesFromZip(*zip_archive.get(),
- location,
- verify,
- verify_checksum,
- error_code,
- error_msg,
- dex_files);
+ bool ok = OpenAllDexFilesFromZip(*zip_archive.get(),
+ location_,
+ verify,
+ verify_checksum,
+ allow_no_dex_files,
+ error_code,
+ error_msg,
+ dex_files);
+ return ok;
}
if (IsMagicValid(magic)) {
+ if (!MapRootContainer(error_msg)) {
+ return false;
+ }
+ const uint8_t* base = root_container_->Begin();
+ size_t size = root_container_->Size();
+ if (size < sizeof(DexFile::Header)) {
+ *error_msg =
+ StringPrintf("DexFile: failed to open dex file '%s' that is too short to have a header",
+ location_.c_str());
+ return false;
+ }
const DexFile::Header* dex_header = reinterpret_cast<const DexFile::Header*>(base);
- std::unique_ptr<const DexFile> dex_file(Open(base,
- size,
- location,
- dex_header->checksum_,
- /*oat_dex_file=*/ nullptr,
- verify,
- verify_checksum,
- error_msg));
+ std::unique_ptr<const DexFile> dex_file = OpenCommon(std::move(root_container_),
+ location_,
+ dex_header->checksum_,
+ /*oat_dex_file=*/nullptr,
+ verify,
+ verify_checksum,
+ error_msg,
+ nullptr);
if (dex_file.get() != nullptr) {
dex_files->push_back(std::move(dex_file));
return true;
@@ -301,13 +321,13 @@
return false;
}
-std::unique_ptr<DexFile> DexFileLoader::OpenCommon(const std::string& location,
+std::unique_ptr<DexFile> DexFileLoader::OpenCommon(std::unique_ptr<DexFileContainer> container,
+ 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) {
CHECK(container != nullptr);
const uint8_t* base = container->Begin();
@@ -322,12 +342,8 @@
if (data_size != 0) {
CHECK_EQ(base, data_base) << "Unsupported for standard dex";
}
- dex_file.reset(new StandardDexFile(base,
- size,
- location,
- location_checksum,
- oat_dex_file,
- std::move(container)));
+ dex_file.reset(new StandardDexFile(
+ base, size, location, location_checksum, oat_dex_file, std::move(container)));
} else if (size >= sizeof(CompactDexFile::Header) && CompactDexFile::IsMagicValid(base)) {
if (data_base == nullptr) {
// TODO: Is there a clean way to support both an explicit data section and reading the one
@@ -351,24 +367,27 @@
*error_msg = "Invalid or truncated dex file";
}
if (dex_file == nullptr) {
- *error_msg = StringPrintf("Failed to open dex file '%s' from memory: %s", location.c_str(),
- error_msg->c_str());
+ *error_msg =
+ StringPrintf("Failed to open dex file '%s': %s", location.c_str(), error_msg->c_str());
return nullptr;
}
if (!dex_file->Init(error_msg)) {
dex_file.reset();
return nullptr;
}
- if (verify && !dex::Verify(dex_file.get(),
- dex_file->Begin(),
- dex_file->Size(),
- location.c_str(),
- verify_checksum,
- error_msg)) {
- if (verify_result != nullptr) {
- *verify_result = VerifyResult::kVerifyFailed;
+ if (verify) {
+ ScopedTrace trace(std::string("Verify dex file ") + location);
+ if (!dex::Verify(dex_file.get(),
+ dex_file->Begin(),
+ dex_file->Size(),
+ location.c_str(),
+ verify_checksum,
+ error_msg)) {
+ if (verify_result != nullptr) {
+ *verify_result = VerifyResult::kVerifyFailed;
+ }
+ return nullptr;
}
- return nullptr;
}
if (verify_result != nullptr) {
*verify_result = VerifyResult::kVerifySucceeded;
@@ -377,7 +396,7 @@
}
std::unique_ptr<const DexFile> DexFileLoader::OpenOneDexFileFromZip(
- const DexZipArchive& zip_archive,
+ const ZipArchive& zip_archive,
const char* entry_name,
const std::string& location,
bool verify,
@@ -385,7 +404,7 @@
DexFileLoaderErrorCode* error_code,
std::string* error_msg) const {
CHECK(!location.empty());
- std::unique_ptr<DexZipEntry> zip_entry(zip_archive.Find(entry_name, error_msg));
+ std::unique_ptr<ZipEntry> zip_entry(zip_archive.Find(entry_name, error_msg));
if (zip_entry == nullptr) {
*error_code = DexFileLoaderErrorCode::kEntryNotFound;
return nullptr;
@@ -396,23 +415,52 @@
return nullptr;
}
- std::vector<uint8_t> map(zip_entry->Extract(error_msg));
- if (map.size() == 0) {
+ CHECK(MemMap::IsInitialized());
+ MemMap map;
+ bool is_file_map = false;
+ if (file_.has_value() && zip_entry->IsUncompressed()) {
+ if (!zip_entry->IsAlignedTo(alignof(DexFile::Header))) {
+ // Do not mmap unaligned ZIP entries because
+ // doing so would fail dex verification which requires 4 byte alignment.
+ LOG(WARNING) << "Can't mmap dex file " << location << "!" << entry_name << " directly; "
+ << "please zipalign to " << alignof(DexFile::Header) << " bytes. "
+ << "Falling back to extracting file.";
+ } else {
+ // Map uncompressed files within zip as file-backed to avoid a dirty copy.
+ map = zip_entry->MapDirectlyFromFile(location.c_str(), /*out*/ error_msg);
+ if (!map.IsValid()) {
+ LOG(WARNING) << "Can't mmap dex file " << location << "!" << entry_name << " directly; "
+ << "is your ZIP file corrupted? Falling back to extraction.";
+ // Try again with Extraction which still has a chance of recovery.
+ }
+ is_file_map = true;
+ }
+ }
+ if (!map.IsValid()) {
+ ScopedTrace trace(std::string("Extract dex file ") + location);
+
+ // Default path for compressed ZIP entries,
+ // and fallback for stored ZIP entries.
+ map = zip_entry->ExtractToMemMap(location.c_str(), entry_name, error_msg);
+ }
+ if (!map.IsValid()) {
*error_msg = StringPrintf("Failed to extract '%s' from '%s': %s", entry_name, location.c_str(),
error_msg->c_str());
*error_code = DexFileLoaderErrorCode::kExtractToMemoryError;
return nullptr;
}
+ auto container = std::make_unique<MemMapContainer>(std::move(map), is_file_map);
+ container->SetIsZip();
+
VerifyResult verify_result;
- std::unique_ptr<const DexFile> dex_file =
- OpenCommon(location,
- zip_entry->GetCrc32(),
- /*oat_dex_file=*/nullptr,
- verify,
- verify_checksum,
- error_msg,
- std::make_unique<VectorContainer>(std::move(map)),
- &verify_result);
+ std::unique_ptr<const DexFile> dex_file = OpenCommon(std::move(container),
+ location,
+ zip_entry->GetCrc32(),
+ /*oat_dex_file=*/nullptr,
+ verify,
+ verify_checksum,
+ error_msg,
+ &verify_result);
if (verify_result != VerifyResult::kVerifySucceeded) {
if (verify_result == VerifyResult::kVerifyNotAttempted) {
*error_code = DexFileLoaderErrorCode::kDexFileError;
@@ -421,6 +469,12 @@
}
return nullptr;
}
+ if (!dex_file->DisableWrite()) {
+ *error_msg = StringPrintf("Failed to make dex file '%s' read only", location.c_str());
+ *error_code = DexFileLoaderErrorCode::kMakeReadOnlyError;
+ return nullptr;
+ }
+ CHECK(dex_file->IsReadOnly()) << location;
*error_code = DexFileLoaderErrorCode::kNoError;
return dex_file;
}
@@ -432,10 +486,11 @@
static constexpr size_t kWarnOnManyDexFilesThreshold = 100;
bool DexFileLoader::OpenAllDexFilesFromZip(
- const DexZipArchive& zip_archive,
+ const ZipArchive& zip_archive,
const std::string& location,
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) const {
@@ -448,6 +503,9 @@
error_code,
error_msg));
if (*error_code != DexFileLoaderErrorCode::kNoError) {
+ if (allow_no_dex_files && *error_code == DexFileLoaderErrorCode::kEntryNotFound) {
+ return true;
+ }
return false;
} else {
// Had at least classes.dex.
diff --git a/libdexfile/dex/dex_file_loader.h b/libdexfile/dex/dex_file_loader.h
index ef45aa8..a2a29bc 100644
--- a/libdexfile/dex/dex_file_loader.h
+++ b/libdexfile/dex/dex_file_loader.h
@@ -18,18 +18,21 @@
#define ART_LIBDEXFILE_DEX_DEX_FILE_LOADER_H_
#include <cstdint>
+#include <functional>
#include <memory>
#include <string>
#include <vector>
+#include "base/os.h"
+#include "base/unix_file/fd_file.h"
+#include "dex_file.h"
+
namespace art {
-class DexFile;
-class DexFileContainer;
class MemMap;
class OatDexFile;
-
-class DexZipArchive;
+class ScopedTrace;
+class ZipArchive;
enum class DexFileLoaderErrorCode {
kNoError,
@@ -103,58 +106,90 @@
return (pos == std::string::npos) ? std::string() : location.substr(pos);
}
- virtual ~DexFileLoader() { }
+ DexFileLoader(const char* filename, int fd, const std::string& location)
+ : filename_(filename),
+ file_(fd == -1 ? std::optional<File>() : File(fd, /*check_usage=*/false)),
+ location_(location) {}
- // Returns the checksums of a file for comparison with GetLocationChecksum().
- // For .dex files, this is the single header checksum.
- // For zip files, this is the zip entry CRC32 checksum for classes.dex and
- // each additional multidex entry classes2.dex, classes3.dex, etc.
- // If a valid zip_fd is provided the file content will be read directly from
- // the descriptor and `filename` will be used as alias for error logging. If
- // zip_fd is -1, the method will try to open the `filename` and read the
- // content from it.
- //
- // The dex_locations vector will be populated with the corresponding multidex
- // locations.
- //
- // Return true if the checksums could be found, false otherwise.
- virtual bool GetMultiDexChecksums(const char* filename,
- std::vector<uint32_t>* checksums,
- std::vector<std::string>* dex_locations,
- std::string* error_msg,
- int zip_fd = -1,
- bool* zip_file_only_contains_uncompress_dex = nullptr) const;
+ DexFileLoader(std::unique_ptr<DexFileContainer>&& container, const std::string& location)
+ : root_container_(std::move(container)), location_(location) {}
- // Opens .dex file, backed by existing vector memory.
- static std::unique_ptr<const DexFile> Open(
- const std::string& location,
- uint32_t location_checksum,
- std::vector<uint8_t>&& memory,
- const OatDexFile* oat_dex_file,
- bool verify,
- bool verify_checksum,
- std::string* error_msg);
+ DexFileLoader(const uint8_t* base, size_t size, const std::string& location);
- // Opens .dex file, backed by existing memory.
- virtual 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) const;
+ DexFileLoader(std::vector<uint8_t>&& memory, const std::string& location);
- // Opens all .dex files found in the memory map, guessing the container format based on file
- // extension.
- virtual bool OpenAll(const uint8_t* base,
- size_t size,
- const std::string& location,
- bool verify,
- bool verify_checksum,
- DexFileLoaderErrorCode* error_code,
- std::string* error_msg,
- std::vector<std::unique_ptr<const DexFile>>* dex_files) const;
+ DexFileLoader(MemMap&& mem_map, const std::string& location);
+
+ DexFileLoader(int fd, const std::string& location)
+ : DexFileLoader(/*filename=*/location.c_str(), fd, location) {}
+
+ DexFileLoader(const char* filename, const std::string& location)
+ : DexFileLoader(filename, /*fd=*/-1, location) {}
+
+ explicit DexFileLoader(const std::string& location)
+ : DexFileLoader(location.c_str(), /*fd=*/-1, location) {}
+
+ virtual ~DexFileLoader() {}
+
+ 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> 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 extension.
+ 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:
enum class VerifyResult { // private
@@ -163,34 +198,47 @@
kVerifyFailed
};
- static std::unique_ptr<DexFile> OpenCommon(const std::string& location,
+ bool InitAndReadMagic(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::unique_ptr<DexFileContainer> container,
+ 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);
- private:
// Open all classesXXX.dex files from a zip archive.
- bool OpenAllDexFilesFromZip(const DexZipArchive& zip_archive,
+ bool OpenAllDexFilesFromZip(const ZipArchive& zip_archive,
const std::string& location,
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) const;
// Opens .dex file from the entry_name in a zip archive. error_code is undefined when non-null
// return.
- std::unique_ptr<const DexFile> OpenOneDexFileFromZip(const DexZipArchive& zip_archive,
+ std::unique_ptr<const DexFile> OpenOneDexFileFromZip(const ZipArchive& zip_archive,
const char* entry_name,
const std::string& location,
bool verify,
bool verify_checksum,
DexFileLoaderErrorCode* error_code,
std::string* error_msg) 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_;
+ std::optional<File> file_;
+ std::unique_ptr<DexFileContainer> root_container_;
+ const std::string location_;
};
} // namespace art
diff --git a/libdexfile/dex/dex_file_loader_test.cc b/libdexfile/dex/dex_file_loader_test.cc
index 30c60b1..381dfdc 100644
--- a/libdexfile/dex/dex_file_loader_test.cc
+++ b/libdexfile/dex/dex_file_loader_test.cc
@@ -217,15 +217,9 @@
// read dex file(s)
static constexpr bool kVerifyChecksum = true;
std::vector<std::unique_ptr<const DexFile>> tmp;
- const DexFileLoader dex_file_loader;
- bool success = dex_file_loader.OpenAll(dex_bytes->data(),
- dex_bytes->size(),
- location,
- /* verify= */ true,
- kVerifyChecksum,
- error_code,
- error_msg,
- dex_files);
+ DexFileLoader dex_file_loader(dex_bytes->data(), dex_bytes->size(), location);
+ bool success =
+ dex_file_loader.Open(/* verify= */ true, kVerifyChecksum, error_code, error_msg, dex_files);
return success;
}
@@ -251,11 +245,8 @@
DecodeDexFile(base64, dex_bytes);
std::string error_message;
- const DexFileLoader dex_file_loader;
- std::unique_ptr<const DexFile> dex_file(dex_file_loader.Open(dex_bytes->data(),
- dex_bytes->size(),
- location,
- location_checksum,
+ DexFileLoader dex_file_loader(dex_bytes->data(), dex_bytes->size(), location);
+ std::unique_ptr<const DexFile> dex_file(dex_file_loader.Open(location_checksum,
/* oat_dex_file= */ nullptr,
/* verify= */ true,
/* verify_checksum= */ true,
@@ -353,15 +344,9 @@
DexFileLoaderErrorCode error_code;
std::string error_msg;
std::vector<std::unique_ptr<const DexFile>> dex_files;
- const DexFileLoader dex_file_loader;
- ASSERT_FALSE(dex_file_loader.OpenAll(dex_bytes.data(),
- dex_bytes.size(),
- kLocationString,
- /* verify= */ true,
- kVerifyChecksum,
- &error_code,
- &error_msg,
- &dex_files));
+ DexFileLoader dex_file_loader(dex_bytes.data(), dex_bytes.size(), kLocationString);
+ ASSERT_FALSE(dex_file_loader.Open(
+ /* verify= */ true, kVerifyChecksum, &error_code, &error_msg, &dex_files));
}
TEST_F(DexFileLoaderTest, ZeroLengthDexRejected) {
@@ -372,15 +357,9 @@
DexFileLoaderErrorCode error_code;
std::string error_msg;
std::vector<std::unique_ptr<const DexFile>> dex_files;
- const DexFileLoader dex_file_loader;
- ASSERT_FALSE(dex_file_loader.OpenAll(dex_bytes.data(),
- dex_bytes.size(),
- kLocationString,
- /* verify= */ true,
- kVerifyChecksum,
- &error_code,
- &error_msg,
- &dex_files));
+ DexFileLoader dex_file_loader(dex_bytes.data(), dex_bytes.size(), kLocationString);
+ ASSERT_FALSE(dex_file_loader.Open(
+ /* verify= */ true, kVerifyChecksum, &error_code, &error_msg, &dex_files));
}
TEST_F(DexFileLoaderTest, GetMultiDexClassesDexName) {
diff --git a/libdexfile/dex/dex_file_verifier_test.cc b/libdexfile/dex/dex_file_verifier_test.cc
index 79e9c8b..ba0b24d 100644
--- a/libdexfile/dex/dex_file_verifier_test.cc
+++ b/libdexfile/dex/dex_file_verifier_test.cc
@@ -104,16 +104,13 @@
// read dex
std::vector<std::unique_ptr<const DexFile>> tmp;
- const DexFileLoader dex_file_loader;
+ DexFileLoader dex_file_loader(dex_bytes.get(), length, location);
DexFileLoaderErrorCode error_code;
- bool success = dex_file_loader.OpenAll(dex_bytes.get(),
- length,
- location,
- /* verify= */ true,
- /* verify_checksum= */ true,
- &error_code,
- error_msg,
- &tmp);
+ bool success = dex_file_loader.Open(/* verify= */ true,
+ /* verify_checksum= */ true,
+ &error_code,
+ error_msg,
+ &tmp);
CHECK(success) << *error_msg;
EXPECT_EQ(1U, tmp.size());
std::unique_ptr<const DexFile> dex_file = std::move(tmp[0]);
diff --git a/libdexfile/dex/test_dex_file_builder.h b/libdexfile/dex/test_dex_file_builder.h
index 283dd48..964f196 100644
--- a/libdexfile/dex/test_dex_file_builder.h
+++ b/libdexfile/dex/test_dex_file_builder.h
@@ -237,14 +237,12 @@
static constexpr bool kVerify = false;
static constexpr bool kVerifyChecksum = false;
std::string error_msg;
- std::unique_ptr<const DexFile> dex_file(DexFileLoader::Open(
- dex_location,
- location_checksum,
- std::move(dex_file_data),
- /*oat_dex_file=*/ nullptr,
- kVerify,
- kVerifyChecksum,
- &error_msg));
+ DexFileLoader dex_file_loader(std::move(dex_file_data), dex_location);
+ std::unique_ptr<const DexFile> dex_file(dex_file_loader.Open(location_checksum,
+ /*oat_dex_file=*/nullptr,
+ kVerify,
+ kVerifyChecksum,
+ &error_msg));
CHECK(dex_file != nullptr) << error_msg;
return dex_file;
}
diff --git a/libdexfile/external/dex_file_ext.cc b/libdexfile/external/dex_file_ext.cc
index 7c0bf2d..c7b1cbb 100644
--- a/libdexfile/external/dex_file_ext.cc
+++ b/libdexfile/external/dex_file_ext.cc
@@ -179,12 +179,9 @@
}
std::string loc_str(location);
- art::DexFileLoader loader;
std::string error_msg;
- std::unique_ptr<const art::DexFile> dex_file = loader.Open(static_cast<const uint8_t*>(address),
- size,
- loc_str,
- header->checksum_,
+ art::DexFileLoader loader(static_cast<const uint8_t*>(address), size, loc_str);
+ std::unique_ptr<const art::DexFile> dex_file = loader.Open(header->checksum_,
/*oat_dex_file=*/nullptr,
/*verify=*/false,
/*verify_checksum=*/false,