Fix size in header, check header alignment and fix checksum and signature

Bug: b/249922554
Change-Id: I3cbfda7b28b3629a294798430a5a15848be731c9
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriterExperimental.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriterExperimental.java
index 77f653a..68f379f 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriterExperimental.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriterExperimental.java
@@ -15,6 +15,7 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.utils.BitUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.ThreadUtils;
@@ -146,6 +147,7 @@
       }
 
       offset = section.getLayout().getEndOfFile();
+      assert BitUtils.isAligned(4, offset);
       sections.add(section);
       fileTiming.end();
       timings.add(fileTiming);
@@ -192,7 +194,32 @@
         List<MapItem> mapItems =
             section
                 .getLayout()
-                .generateMapInfo(section.getFileWriter(), stringIdsSize, stringIdsOffset);
+                .generateMapInfo(
+                    section.getFileWriter(),
+                    section.getLayout().headerOffset,
+                    stringIdsSize,
+                    stringIdsOffset,
+                    lastSection.getLayout().getStringDataOffsets());
+        int originalSize = dexOutputBuffer.getInt();
+        int size = 0;
+        for (MapItem mapItem : mapItems) {
+          size += mapItem.write(dexOutputBuffer);
+        }
+        assert originalSize == size;
+        // Calculate signature and checksum after the map is written.
+        section.getFileWriter().writeSignature(section.getLayout(), dexOutputBuffer);
+        section.getFileWriter().writeChecksum(section.getLayout(), dexOutputBuffer);
+      } else {
+        dexOutputBuffer.moveTo(section.getLayout().getMapOffset());
+        List<MapItem> mapItems =
+            section
+                .getLayout()
+                .generateMapInfo(
+                    section.getFileWriter(),
+                    section.getLayout().headerOffset,
+                    stringIdsSize,
+                    stringIdsOffset,
+                    lastSection.getLayout().getStringDataOffsets());
         int originalSize = dexOutputBuffer.getInt();
         int size = 0;
         for (MapItem mapItem : mapItems) {
@@ -214,6 +241,7 @@
       DexOutputBuffer outputBuffer,
       boolean last) {
     assert !virtualFile.isEmpty();
+    assert BitUtils.isAligned(4, offset);
     printItemUseInfo(virtualFile);
 
     timing.begin("Reindex for lazy strings");
diff --git a/src/main/java/com/android/tools/r8/dex/FileWriter.java b/src/main/java/com/android/tools/r8/dex/FileWriter.java
index 5d8832f..86182c2 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -815,7 +815,7 @@
     dest.putByte(Constants.DEX_FILE_MAGIC_SUFFIX);
     // Leave out checksum and signature for now.
     dest.moveTo(layout.headerOffset + Constants.FILE_SIZE_OFFSET);
-    dest.putInt(layout.getEndOfFile());
+    dest.putInt(layout.getEndOfFile() - layout.headerOffset);
     dest.putInt(Constants.TYPE_HEADER_ITEM_SIZE);
     dest.putInt(Constants.ENDIAN_CONSTANT);
     dest.putInt(0);
@@ -918,7 +918,7 @@
     }
 
     public int size() {
-      return length == 0 ? 0 : 1;
+      return length == 0 && type != Constants.TYPE_STRING_DATA_ITEM ? 0 : 1;
     }
   }
 
@@ -1129,13 +1129,21 @@
 
     public List<MapItem> generateMapInfo(FileWriter fileWriter) {
       return generateMapInfo(
-          fileWriter, fileWriter.mixedSectionOffsets.getStringData().size(), stringIdsOffset);
+          fileWriter,
+          0,
+          fileWriter.mixedSectionOffsets.getStringData().size(),
+          stringIdsOffset,
+          getStringDataOffsets());
     }
 
     public List<MapItem> generateMapInfo(
-        FileWriter fileWriter, int stringIdsSize, int stringIdsOffset) {
+        FileWriter fileWriter,
+        int headerOffset,
+        int stringIdsSize,
+        int stringIdsOffset,
+        int stringDataOffsets) {
       List<MapItem> mapItems = new ArrayList<>();
-      mapItems.add(new MapItem(Constants.TYPE_HEADER_ITEM, 0, 1));
+      mapItems.add(new MapItem(Constants.TYPE_HEADER_ITEM, headerOffset, 1));
       mapItems.add(
           new MapItem(
               Constants.TYPE_STRING_ID_ITEM,
@@ -1188,8 +1196,8 @@
       mapItems.add(
           new MapItem(
               Constants.TYPE_STRING_DATA_ITEM,
-              getStringDataOffsets(),
-              getStringDataOffsets() == 0 ? 0 : stringIdsSize));
+              stringDataOffsets,
+              stringDataOffsets == 0 ? 0 : stringIdsSize));
       mapItems.add(
           new MapItem(
               Constants.TYPE_ANNOTATION_ITEM,
diff --git a/src/main/java/com/android/tools/r8/utils/BitUtils.java b/src/main/java/com/android/tools/r8/utils/BitUtils.java
index b264b4b..f951031 100644
--- a/src/main/java/com/android/tools/r8/utils/BitUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/BitUtils.java
@@ -13,4 +13,9 @@
   public static boolean isBitInMaskSet(int value, int mask) {
     return (value & mask) != 0;
   }
+
+  public static boolean isAligned(int alignment, int value) {
+    assert (alignment & (alignment - 1)) == 0; // Check alignment is power of 2.
+    return (value & (alignment - 1)) == 0;
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/dex/container/DexContainerFormatBasicTest.java b/src/test/java/com/android/tools/r8/dex/container/DexContainerFormatBasicTest.java
index 72f856f..41aa6d0 100644
--- a/src/test/java/com/android/tools/r8/dex/container/DexContainerFormatBasicTest.java
+++ b/src/test/java/com/android/tools/r8/dex/container/DexContainerFormatBasicTest.java
@@ -28,8 +28,10 @@
 import com.android.tools.r8.maindexlist.MainDexListTests;
 import com.android.tools.r8.transformers.ClassTransformer;
 import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.BitUtils;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.DexVersion;
+import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.ZipUtils;
 import com.google.common.collect.ImmutableList;
 import com.google.common.io.ByteStreams;
@@ -160,10 +162,13 @@
     IntList sections = new IntArrayList();
     int offset = 0;
     while (offset < buffer.capacity()) {
+      assertTrue(BitUtils.isAligned(4, offset));
       sections.add(offset);
       int dataSize = buffer.getInt(offset + DATA_SIZE_OFFSET);
       int dataOffset = buffer.getInt(offset + DATA_OFF_OFFSET);
+      int file_size = buffer.getInt(offset + FILE_SIZE_OFFSET);
       offset = dataOffset + dataSize;
+      assertEquals(file_size, offset - ListUtils.last(sections));
     }
     assertEquals(buffer.capacity(), offset);
 
@@ -229,9 +234,7 @@
     int sectionSize = buffer.getInt(offset + FILE_SIZE_OFFSET);
     MessageDigest md = MessageDigest.getInstance("SHA-1");
     md.update(
-        buffer.asByteBuffer().array(),
-        offset + FILE_SIZE_OFFSET,
-        sectionSize - offset - FILE_SIZE_OFFSET);
+        buffer.asByteBuffer().array(), offset + FILE_SIZE_OFFSET, sectionSize - FILE_SIZE_OFFSET);
     byte[] expectedSignature = new byte[20];
     md.digest(expectedSignature, 0, 20);
     for (int i = 0; i < expectedSignature.length; i++) {
@@ -243,9 +246,7 @@
     int sectionSize = buffer.getInt(offset + FILE_SIZE_OFFSET);
     Adler32 adler = new Adler32();
     adler.update(
-        buffer.asByteBuffer().array(),
-        offset + SIGNATURE_OFFSET,
-        sectionSize - offset - SIGNATURE_OFFSET);
+        buffer.asByteBuffer().array(), offset + SIGNATURE_OFFSET, sectionSize - SIGNATURE_OFFSET);
     assertEquals((int) adler.getValue(), buffer.getInt(offset + CHECKSUM_OFFSET));
   }