Add dex container support to dexter am: 68fef98d8c am: fbeb8a3fad

Original change: https://android-review.googlesource.com/c/platform/tools/dexter/+/2740254

Change-Id: Ic66b05ddb5df54077b935287b94a7590970c6b12
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/slicer/dex_format.cc b/slicer/dex_format.cc
index b1a1ab0..16b36a0 100644
--- a/slicer/dex_format.cc
+++ b/slicer/dex_format.cc
@@ -19,10 +19,18 @@
 #include "slicer/common.h"
 
 #include <sstream>
+#include <cstdlib>
 #include <zlib.h>
 
 namespace dex {
 
+// The expected format of the magic is dex\nXXX\0 where XXX are digits. We extract this value.
+// Returns 0 if the version can not be parsed.
+u4 Header::GetVersion(const void* magic) {
+  const char* version = reinterpret_cast<const char*>(magic) + 4;
+  return version[3] == '\0' ? strtol(version, nullptr, 10) : 0;
+}
+
 // Compute the DEX file checksum for a memory-mapped DEX file
 u4 ComputeChecksum(const Header* header) {
   const u1* start = reinterpret_cast<const u1*>(header);
diff --git a/slicer/export/slicer/dex_format.h b/slicer/export/slicer/dex_format.h
index 4e70bd1..e73d1dc 100644
--- a/slicer/export/slicer/dex_format.h
+++ b/slicer/export/slicer/dex_format.h
@@ -145,6 +145,36 @@
 
 // "header_item"
 struct Header {
+  static constexpr size_t kV40Size = 0x70;  // Same as all previous dex versions.
+  static constexpr size_t kV41Size = 0x78;  // Added container_{size,off} fields.
+                                            // See http://go/dex-container-format
+  static constexpr size_t kMaxSize = kV41Size;
+
+  static constexpr u4 kV41 = 41;
+  static constexpr u4 kMinVersion = 35;  // Minimum supported dex version.
+  static constexpr u4 kMaxVersion = 41;  // Maximum supported dex version.
+
+  // Parse magic number and extract the version integer. E.g.: `dex\n123\0` returns `123`.
+  // Returns 0 upon failure.
+  static u4 GetVersion(const void* magic);
+
+  u4 GetVersion() const {
+    return GetVersion(magic);
+  }
+
+  u4 ContainerSize() const {
+    return header_size >= kV41Size ? container_size : file_size;
+  }
+
+  u4 ContainerOff() const {
+    return header_size >= kV41Size ? container_off : 0;
+  }
+
+  void SetContainer(u4 off, u4 size) {
+    container_off = off;
+    container_size = size;
+  }
+
   u1 magic[8];
   u4 checksum;
   u1 signature[kSHA1DigestLen];
@@ -168,6 +198,10 @@
   u4 class_defs_off;
   u4 data_size;
   u4 data_off;
+
+ private:
+  u4 container_size;
+  u4 container_off;
 };
 
 // "map_item"
diff --git a/slicer/reader.cc b/slicer/reader.cc
index 6358315..14c43d7 100644
--- a/slicer/reader.cc
+++ b/slicer/reader.cc
@@ -1001,15 +1001,24 @@
 
 // Basic .dex header structural checks
 void Reader::ValidateHeader() {
-  SLICER_CHECK_GT(size_, sizeof(dex::Header));
+  SLICER_CHECK_GT(size_, dex::Header::kV40Size);
 
   // Known issue: For performance reasons the initial size_ passed to jvmti events might be an
   // estimate. b/72402467
   SLICER_CHECK_LE(header_->file_size, size_);
-  SLICER_CHECK_EQ(header_->header_size, sizeof(dex::Header));
+  // Check that we support this version of dex header
+  SLICER_CHECK(
+      header_->header_size == dex::Header::kV40Size ||
+      header_->header_size == dex::Header::kV41Size);
   SLICER_CHECK_EQ(header_->endian_tag, dex::kEndianConstant);
   SLICER_CHECK_EQ(header_->data_size % 4, 0);
 
+  // If the dex file is within container with other dex files,
+  // adjust the base address to the start of the container.
+  SLICER_CHECK_LE(header_->ContainerSize() - header_->ContainerOff(), size_);
+  image_ -= header_->ContainerOff();
+  size_ = header_->ContainerSize();
+
   // Known issue: The fields might be slighly corrupted b/65452964
   // SLICER_CHECK_LE(header_->data_off + header_->data_size, size_);
 
@@ -1021,7 +1030,8 @@
   SLICER_CHECK_EQ(header_->field_ids_off % 4, 0);
   SLICER_CHECK_EQ(header_->method_ids_off % 4, 0);
   SLICER_CHECK_EQ(header_->class_defs_off % 4, 0);
-  SLICER_CHECK(header_->map_off >= header_->data_off && header_->map_off < size_);
+  SLICER_CHECK_GE(header_->map_off, header_->data_off);
+  SLICER_CHECK_LT(header_->map_off, size_);
   SLICER_CHECK_EQ(header_->link_size, 0);
   SLICER_CHECK_EQ(header_->link_off, 0);
   SLICER_CHECK_EQ(header_->data_off % 4, 0);
diff --git a/slicer/writer.cc b/slicer/writer.cc
index bed17fb..c1f68a9 100644
--- a/slicer/writer.cc
+++ b/slicer/writer.cc
@@ -226,7 +226,7 @@
   SLICER_CHECK_GT(section.ItemsCount(), 0);
   dex::u4 offset = section.SectionOffset();
   dex::u4 size = section.size();
-  SLICER_CHECK_GE(offset, sizeof(dex::Header));
+  SLICER_CHECK_GE(offset, dex::Header::kV40Size);
   SLICER_CHECK_LE(offset + size, image_size);
 
   ::memcpy(image + offset, section.data(), size);
@@ -253,12 +253,18 @@
   // (ideally we shouldn't change the IR while generating an image)
   dex_ir_->Normalize();
 
+  int version = Header::GetVersion(dex_ir_->magic.ptr());
+  SLICER_CHECK_NE(version, 0);
+  SLICER_CHECK_GE(version, Header::kMinVersion);
+  SLICER_CHECK_LE(version, Header::kMaxVersion);
+  u4 header_size = version >= Header::kV41 ? Header::kV41Size : Header::kV40Size;
+
   // track the current offset within the .dex image
   dex::u4 offset = 0;
 
   // allocate the image and index sections
   // (they will be back-filled)
-  offset += sizeof(dex::Header);
+  offset += header_size;
   offset += dex_->string_ids.Init(offset, dex_ir_->strings.size());
   offset += dex_->type_ids.Init(offset, dex_ir_->types.size());
   offset += dex_->proto_ids.Init(offset, dex_ir_->protos.size());
@@ -304,7 +310,7 @@
   memset(image, 0, image_size);
 
   // finally, back-fill the header
-  SLICER_CHECK_GT(image_size, sizeof(dex::Header));
+  SLICER_CHECK_GT(image_size, header_size);
 
   dex::Header* header = reinterpret_cast<dex::Header*>(image + 0);
 
@@ -312,7 +318,7 @@
   memcpy(header->magic, dex_ir_->magic.ptr(), dex_ir_->magic.size());
 
   header->file_size = image_size;
-  header->header_size = sizeof(dex::Header);
+  header->header_size = header_size;
   header->endian_tag = dex::kEndianConstant;
 
   header->link_size = 0;
@@ -333,6 +339,11 @@
   header->class_defs_off = dex_->class_defs.SectionOffset();
   header->data_size = image_size - data_offset;
   header->data_off = data_offset;
+  if (version >= Header::kV41) {
+    header->data_size = 0;
+    header->data_off = 0;
+    header->SetContainer(0, header->file_size);
+  }
 
   // copy the individual sections to the final image
   CopySection(dex_->string_ids, image, image_size);
@@ -384,7 +395,7 @@
 template <class T>
 static void AddMapItem(const T& section, std::vector<dex::MapItem>& items) {
   if (section.ItemsCount() > 0) {
-    SLICER_CHECK_GE(section.SectionOffset(), sizeof(dex::Header));
+    SLICER_CHECK_GE(section.SectionOffset(), dex::Header::kV40Size);
     dex::MapItem map_item = {};
     map_item.type = section.MapEntryType();
     map_item.size = section.ItemsCount();