Build dexdump2 and dexlist without libart

Finally, flesh out libdexfile enough that it can replace libart for use
in dexdump2.

Bug: 22322814
Test: make -j 50 dexdump2 dexlist
      dexdump2 -d fb/base.apk
      dexlist fb/base.apk

Change-Id: Ibf9a1cd642cd473eea0b2208d72b4768aaa17f02
diff --git a/runtime/dex/dex_file_loader.cc b/runtime/dex/dex_file_loader.cc
index c80ea19..ccad19f 100644
--- a/runtime/dex/dex_file_loader.cc
+++ b/runtime/dex/dex_file_loader.cc
@@ -16,25 +16,143 @@
 
 #include "dex_file_loader.h"
 
-// #include <sys/mman.h>  // For the PROT_* and MAP_* constants.
-// #include <sys/stat.h>
-
 #include "android-base/stringprintf.h"
 
-#include "base/file_magic.h"
 #include "base/stl_util.h"
-// #include "base/systrace.h"
-// #include "base/unix_file/fd_file.h"
 #include "compact_dex_file.h"
 #include "dex_file.h"
 #include "dex_file_verifier.h"
 #include "standard_dex_file.h"
-// #include "zip_archive.h"
+#include "ziparchive/zip_archive.h"
+
+// system/core/zip_archive definitions.
+struct ZipEntry;
+typedef void* ZipArchiveHandle;
 
 namespace art {
 
+namespace {
+
+class VectorContainer : public DexFileContainer {
+ public:
+  explicit VectorContainer(std::vector<uint8_t>&& vector) : vector_(std::move(vector)) { }
+  virtual ~VectorContainer() OVERRIDE { }
+
+  int GetPermissions() OVERRIDE {
+    return 0;
+  }
+
+  bool IsReadOnly() OVERRIDE {
+    return true;
+  }
+
+  bool EnableWrite() OVERRIDE {
+    return false;
+  }
+
+  bool DisableWrite() OVERRIDE {
+    return false;
+  }
+
+ private:
+  std::vector<uint8_t> vector_;
+  DISALLOW_COPY_AND_ASSIGN(VectorContainer);
+};
+
+}  // 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_, ZipString(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));
 }
@@ -114,80 +232,47 @@
                     /*verify_result*/ nullptr);
 }
 
-std::unique_ptr<const DexFile> DexFileLoader::Open(const std::string& location ATTRIBUTE_UNUSED,
-                                                   uint32_t location_checksum ATTRIBUTE_UNUSED,
-                                                   std::unique_ptr<MemMap> map ATTRIBUTE_UNUSED,
-                                                   bool verify ATTRIBUTE_UNUSED,
-                                                   bool verify_checksum ATTRIBUTE_UNUSED,
-                                                   std::string* error_msg) const {
-  *error_msg = "UNIMPLEMENTED";
-  return nullptr;
-}
-
-bool DexFileLoader::Open(
-    const char* filename ATTRIBUTE_UNUSED,
-    const std::string& location ATTRIBUTE_UNUSED,
-    bool verify ATTRIBUTE_UNUSED,
-    bool verify_checksum ATTRIBUTE_UNUSED,
+bool DexFileLoader::OpenAll(
+    const uint8_t* base,
+    size_t size,
+    const std::string& location,
+    bool verify,
+    bool verify_checksum,
     std::string* error_msg,
-    std::vector<std::unique_ptr<const DexFile>>* dex_files ATTRIBUTE_UNUSED) const {
-  *error_msg = "UNIMPLEMENTED";
-  return false;
-}
-
-std::unique_ptr<const DexFile> DexFileLoader::OpenDex(
-    int fd ATTRIBUTE_UNUSED,
-    const std::string& location ATTRIBUTE_UNUSED,
-    bool verify ATTRIBUTE_UNUSED,
-    bool verify_checksum ATTRIBUTE_UNUSED,
-    bool mmap_shared ATTRIBUTE_UNUSED,
-    std::string* error_msg) const {
-  *error_msg = "UNIMPLEMENTED";
-  return nullptr;
-}
-
-bool DexFileLoader::OpenZip(
-    int fd ATTRIBUTE_UNUSED,
-    const std::string& location ATTRIBUTE_UNUSED,
-    bool verify ATTRIBUTE_UNUSED,
-    bool verify_checksum ATTRIBUTE_UNUSED,
-    std::string* error_msg,
-    std::vector<std::unique_ptr<const DexFile>>* dex_files ATTRIBUTE_UNUSED) const {
-  *error_msg = "UNIMPLEMENTED";
-  return false;
-}
-
-std::unique_ptr<const DexFile> DexFileLoader::OpenFile(
-    int fd ATTRIBUTE_UNUSED,
-    const std::string& location ATTRIBUTE_UNUSED,
-    bool verify ATTRIBUTE_UNUSED,
-    bool verify_checksum ATTRIBUTE_UNUSED,
-    bool mmap_shared ATTRIBUTE_UNUSED,
-    std::string* error_msg) const {
-  *error_msg = "UNIMPLEMENTED";
-  return nullptr;
-}
-
-std::unique_ptr<const DexFile> DexFileLoader::OpenOneDexFileFromZip(
-    const ZipArchive& zip_archive ATTRIBUTE_UNUSED,
-    const char* entry_name ATTRIBUTE_UNUSED,
-    const std::string& location ATTRIBUTE_UNUSED,
-    bool verify ATTRIBUTE_UNUSED,
-    bool verify_checksum ATTRIBUTE_UNUSED,
-    std::string* error_msg,
-    ZipOpenErrorCode* error_code ATTRIBUTE_UNUSED) const {
-  *error_msg = "UNIMPLEMENTED";
-  return nullptr;
-}
-
-bool DexFileLoader::OpenAllDexFilesFromZip(
-    const ZipArchive& zip_archive ATTRIBUTE_UNUSED,
-    const std::string& location ATTRIBUTE_UNUSED,
-    bool verify ATTRIBUTE_UNUSED,
-    bool verify_checksum ATTRIBUTE_UNUSED,
-    std::string* error_msg,
-    std::vector<std::unique_ptr<const DexFile>>* dex_files ATTRIBUTE_UNUSED) const {
-  *error_msg = "UNIMPLEMENTED";
+    std::vector<std::unique_ptr<const DexFile>>* dex_files) const {
+  DCHECK(dex_files != nullptr) << "DexFile::Open: out-param is nullptr";
+  uint32_t magic = *reinterpret_cast<const uint32_t*>(base);
+  if (IsZipMagic(magic)) {
+    std::unique_ptr<DexZipArchive> zip_archive(DexZipArchive::Open(base, size, error_msg));
+    if (zip_archive.get() == nullptr) {
+      DCHECK(!error_msg->empty());
+      return false;
+    }
+    return OpenAllDexFilesFromZip(*zip_archive.get(),
+                                  location,
+                                  verify,
+                                  verify_checksum,
+                                  error_msg,
+                                  dex_files);
+  }
+  if (IsMagicValid(magic)) {
+    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));
+    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");
   return false;
 }
 
@@ -238,4 +323,125 @@
   return dex_file;
 }
 
+std::unique_ptr<const DexFile> DexFileLoader::OpenOneDexFileFromZip(
+    const DexZipArchive& zip_archive,
+    const char* entry_name,
+    const std::string& location,
+    bool verify,
+    bool verify_checksum,
+    std::string* error_msg,
+    ZipOpenErrorCode* error_code) const {
+  CHECK(!location.empty());
+  std::unique_ptr<DexZipEntry> zip_entry(zip_archive.Find(entry_name, error_msg));
+  if (zip_entry == nullptr) {
+    *error_code = ZipOpenErrorCode::kEntryNotFound;
+    return nullptr;
+  }
+  if (zip_entry->GetUncompressedLength() == 0) {
+    *error_msg = StringPrintf("Dex file '%s' has zero length", location.c_str());
+    *error_code = ZipOpenErrorCode::kDexFileError;
+    return nullptr;
+  }
+
+  std::vector<uint8_t> map(zip_entry->Extract(error_msg));
+  if (map.size() == 0) {
+    *error_msg = StringPrintf("Failed to extract '%s' from '%s': %s", entry_name, location.c_str(),
+                              error_msg->c_str());
+    *error_code = ZipOpenErrorCode::kExtractToMemoryError;
+    return nullptr;
+  }
+  VerifyResult verify_result;
+  std::unique_ptr<const DexFile> dex_file = OpenCommon(map.data(),
+                                                       map.size(),
+                                                       location,
+                                                       zip_entry->GetCrc32(),
+                                                       /*oat_dex_file*/ nullptr,
+                                                       verify,
+                                                       verify_checksum,
+                                                       error_msg,
+                                                       new VectorContainer(std::move(map)),
+                                                       &verify_result);
+  if (dex_file == nullptr) {
+    if (verify_result == VerifyResult::kVerifyNotAttempted) {
+      *error_code = ZipOpenErrorCode::kDexFileError;
+    } else {
+      *error_code = ZipOpenErrorCode::kVerifyError;
+    }
+    return nullptr;
+  }
+  if (verify_result != VerifyResult::kVerifySucceeded) {
+    *error_code = ZipOpenErrorCode::kVerifyError;
+    return nullptr;
+  }
+  *error_code = ZipOpenErrorCode::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 DexFileLoader::OpenAllDexFilesFromZip(
+    const DexZipArchive& zip_archive,
+    const std::string& location,
+    bool verify,
+    bool verify_checksum,
+    std::string* error_msg,
+    std::vector<std::unique_ptr<const DexFile>>* dex_files) const {
+  DCHECK(dex_files != nullptr) << "DexFile::OpenFromZip: out-param is nullptr";
+  ZipOpenErrorCode error_code;
+  std::unique_ptr<const DexFile> dex_file(OpenOneDexFileFromZip(zip_archive,
+                                                                kClassesDex,
+                                                                location,
+                                                                verify,
+                                                                verify_checksum,
+                                                                error_msg,
+                                                                &error_code));
+  if (dex_file.get() == nullptr) {
+    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 != ZipOpenErrorCode::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