Update DEX container format header
* Add the additional header offset field to the V41 header
* Set data offset and length to 0 for V41
Bug: b/249922554
Change-Id: I22a801005ca9a7784f2628cc72eda0344acebcb6
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 68f379f..6087898 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriterExperimental.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriterExperimental.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.dex;
+import static com.android.tools.r8.utils.DexVersion.Layout.CONTAINER_DEX;
+
import com.android.tools.r8.ByteBufferProvider;
import com.android.tools.r8.ByteDataView;
import com.android.tools.r8.DexFilePerClassFileConsumer;
@@ -274,6 +276,6 @@
// Collect the non-fixed sections.
timing.time("collect", fileWriter::collect);
// Generate and write the bytes.
- return timing.time("generate", () -> fileWriter.generate(offset));
+ return timing.time("generate", () -> fileWriter.generate(offset, CONTAINER_DEX));
}
}
diff --git a/src/main/java/com/android/tools/r8/dex/Constants.java b/src/main/java/com/android/tools/r8/dex/Constants.java
index 7cf3ca9..912f4f1 100644
--- a/src/main/java/com/android/tools/r8/dex/Constants.java
+++ b/src/main/java/com/android/tools/r8/dex/Constants.java
@@ -45,12 +45,14 @@
public static final int CLASS_DEFS_OFF_OFFSET = CLASS_DEFS_SIZE_OFFSET + 4;
public static final int DATA_SIZE_OFFSET = CLASS_DEFS_OFF_OFFSET + 4;
public static final int DATA_OFF_OFFSET = DATA_SIZE_OFFSET + 4;
+ public static final int HEADER_OFF_OFFSET = DATA_OFF_OFFSET + 4;
public static final int ENDIAN_CONSTANT = 0x12345678;
public static final int REVERSE_ENDIAN_CONSTANT = 0x78563412;
public static final int TYPE_HEADER_ITEM = 0x0;
public static final int TYPE_HEADER_ITEM_SIZE = 0x70;
+ public static final int TYPE_HEADER_ITEM_SIZE_V41 = 0x74;
public static final int TYPE_STRING_ID_ITEM = 0x0001;
public static final int TYPE_STRING_ID_ITEM_SIZE = 0x04;
public static final int TYPE_TYPE_ID_ITEM = 0x0002;
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 86182c2..f36a045 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.dex;
+import static com.android.tools.r8.utils.DexVersion.Layout.SINGLE_DEX;
import static com.android.tools.r8.utils.LebUtils.sizeAsUleb128;
import com.android.tools.r8.ByteBufferProvider;
@@ -206,18 +207,18 @@
}
public ByteBufferResult generate() {
- DexContainerSection res = generate(0);
+ DexContainerSection res = generate(0, SINGLE_DEX);
return new ByteBufferResult(res.getBuffer().stealByteBuffer(), res.getLayout().getEndOfFile());
}
- public DexContainerSection generate(int offset) {
+ public DexContainerSection generate(int offset, DexVersion.Layout layoutType) {
// Check restrictions on interface methods.
checkInterfaceMethods();
// Check restriction on the names of fields, methods and classes
assert verifyNames();
- Layout layout = Layout.from(mapping, offset, includeStringData);
+ Layout layout = Layout.from(mapping, offset, layoutType, includeStringData);
layout.setCodesOffset(layout.dataSectionOffset);
// Sort the codes first, as their order might impact size due to alignment constraints.
@@ -298,7 +299,7 @@
layout.setEndOfFile(dest.position());
// Now that we have all mixedSectionOffsets, lets write the indexed items.
- dest.moveTo(layout.headerOffset + Constants.TYPE_HEADER_ITEM_SIZE);
+ dest.moveTo(layout.headerOffset + layout.getHeaderSize());
if (includeStringData) {
writeFixedSectionItems(mapping.getStrings(), layout.stringIdsOffset, this::writeStringItem);
} else {
@@ -816,7 +817,7 @@
// Leave out checksum and signature for now.
dest.moveTo(layout.headerOffset + Constants.FILE_SIZE_OFFSET);
dest.putInt(layout.getEndOfFile() - layout.headerOffset);
- dest.putInt(Constants.TYPE_HEADER_ITEM_SIZE);
+ dest.putInt(layout.getHeaderSize());
dest.putInt(Constants.ENDIAN_CONSTANT);
dest.putInt(0);
dest.putInt(0);
@@ -839,8 +840,14 @@
int numberOfClasses = mapping.getClasses().length;
dest.putInt(numberOfClasses);
dest.putInt(numberOfClasses == 0 ? 0 : layout.classDefsOffset);
- dest.putInt(layout.getDataSectionSize());
- dest.putInt(layout.dataSectionOffset);
+ if (layout.isContainerSection()) {
+ dest.putInt(0);
+ dest.putInt(0);
+ dest.putInt(layout.headerOffset);
+ } else {
+ dest.putInt(layout.getDataSectionSize());
+ dest.putInt(layout.dataSectionOffset);
+ }
assert dest.position() == layout.stringIdsOffset;
}
@@ -937,6 +944,7 @@
final int callSiteIdsOffset;
final int methodHandleIdsOffset;
final int dataSectionOffset;
+ final DexVersion.Layout layoutType;
// Mixed size sections
private int codesOffset = NOT_SET; // aligned
@@ -963,7 +971,8 @@
int classDefsOffset,
int callSiteIdsOffset,
int methodHandleIdsOffset,
- int dataSectionOffset) {
+ int dataSectionOffset,
+ DexVersion.Layout layoutType) {
this.headerOffset = headerOffset;
this.stringIdsOffset = stringIdsOffset;
this.typeIdsOffset = typeIdsOffset;
@@ -974,6 +983,7 @@
this.callSiteIdsOffset = callSiteIdsOffset;
this.methodHandleIdsOffset = methodHandleIdsOffset;
this.dataSectionOffset = dataSectionOffset;
+ this.layoutType = layoutType;
assert stringIdsOffset <= typeIdsOffset;
assert typeIdsOffset <= protoIdsOffset;
assert protoIdsOffset <= fieldIdsOffset;
@@ -985,13 +995,18 @@
}
static Layout from(ObjectToOffsetMapping mapping) {
- return from(mapping, 0, true);
+ return from(mapping, 0, SINGLE_DEX, true);
}
- static Layout from(ObjectToOffsetMapping mapping, int offset, boolean includeStringData) {
+ static Layout from(
+ ObjectToOffsetMapping mapping,
+ int offset,
+ DexVersion.Layout layoutType,
+ boolean includeStringData) {
+ assert offset == 0 || layoutType.isContainer();
return new Layout(
offset,
- offset += Constants.TYPE_HEADER_ITEM_SIZE,
+ offset += layoutType.getHeaderSize(),
offset +=
includeStringData
? mapping.getStrings().size() * Constants.TYPE_STRING_ID_ITEM_SIZE
@@ -1002,7 +1017,8 @@
offset += mapping.getMethods().size() * Constants.TYPE_METHOD_ID_ITEM_SIZE,
offset += mapping.getClasses().length * Constants.TYPE_CLASS_DEF_ITEM_SIZE,
offset += mapping.getCallSites().size() * Constants.TYPE_CALL_SITE_ID_ITEM_SIZE,
- offset += mapping.getMethodHandles().size() * Constants.TYPE_METHOD_HANDLE_ITEM_SIZE);
+ offset += mapping.getMethodHandles().size() * Constants.TYPE_METHOD_HANDLE_ITEM_SIZE,
+ layoutType);
}
int getDataSectionSize() {
@@ -1127,6 +1143,14 @@
this.mapOffset = mapOffset;
}
+ public boolean isContainerSection() {
+ return layoutType.isContainer();
+ }
+
+ public int getHeaderSize() {
+ return layoutType.getHeaderSize();
+ }
+
public List<MapItem> generateMapInfo(FileWriter fileWriter) {
return generateMapInfo(
fileWriter,
diff --git a/src/main/java/com/android/tools/r8/utils/DexVersion.java b/src/main/java/com/android/tools/r8/utils/DexVersion.java
index e1b4588..cbfd8fc 100644
--- a/src/main/java/com/android/tools/r8/utils/DexVersion.java
+++ b/src/main/java/com/android/tools/r8/utils/DexVersion.java
@@ -3,26 +3,49 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.utils;
+import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.utils.structural.Ordered;
import java.util.Optional;
/** Android dex version */
public enum DexVersion implements Ordered<DexVersion> {
- V35(35, new byte[] {'0', '3', '5'}),
- V37(37, new byte[] {'0', '3', '7'}),
- V38(38, new byte[] {'0', '3', '8'}),
- V39(39, new byte[] {'0', '3', '9'}),
- V40(40, new byte[] {'0', '4', '0'}),
- V41(41, new byte[] {'0', '4', '1'});
+ V35(35, new byte[] {'0', '3', '5'}, Layout.SINGLE_DEX),
+ V37(37, new byte[] {'0', '3', '7'}, Layout.SINGLE_DEX),
+ V38(38, new byte[] {'0', '3', '8'}, Layout.SINGLE_DEX),
+ V39(39, new byte[] {'0', '3', '9'}, Layout.SINGLE_DEX),
+ V40(40, new byte[] {'0', '4', '0'}, Layout.SINGLE_DEX),
+ V41(41, new byte[] {'0', '4', '1'}, Layout.CONTAINER_DEX);
+
+ public enum Layout {
+ SINGLE_DEX,
+ CONTAINER_DEX;
+
+ public boolean isContainer() {
+ return this == CONTAINER_DEX;
+ }
+
+ public int getHeaderSize() {
+ return isContainer() ? Constants.TYPE_HEADER_ITEM_SIZE_V41 : Constants.TYPE_HEADER_ITEM_SIZE;
+ }
+ }
private final int dexVersion;
-
private final byte[] dexVersionBytes;
+ private final Layout layout;
- DexVersion(int dexVersion, byte[] dexVersionBytes) {
+ DexVersion(int dexVersion, byte[] dexVersionBytes, Layout layout) {
this.dexVersion = dexVersion;
this.dexVersionBytes = dexVersionBytes;
+ this.layout = layout;
+ }
+
+ public Layout getLayout() {
+ return layout;
+ }
+
+ public boolean isContainerDex() {
+ return getLayout() == Layout.CONTAINER_DEX;
}
public int getIntValue() {
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 41aa6d0..28a01e0 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
@@ -8,6 +8,8 @@
import static com.android.tools.r8.dex.Constants.DATA_SIZE_OFFSET;
import static com.android.tools.r8.dex.Constants.DEX_MAGIC_SIZE;
import static com.android.tools.r8.dex.Constants.FILE_SIZE_OFFSET;
+import static com.android.tools.r8.dex.Constants.HEADER_OFF_OFFSET;
+import static com.android.tools.r8.dex.Constants.HEADER_SIZE_OFFSET;
import static com.android.tools.r8.dex.Constants.MAP_OFF_OFFSET;
import static com.android.tools.r8.dex.Constants.SIGNATURE_OFFSET;
import static com.android.tools.r8.dex.Constants.STRING_IDS_OFF_OFFSET;
@@ -31,7 +33,6 @@
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;
@@ -167,8 +168,10 @@
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));
+ if (!expectedVersion.isContainerDex()) {
+ assertEquals(file_size, dataOffset + dataSize);
+ }
+ offset += expectedVersion.isContainerDex() ? file_size : dataOffset + dataSize;
}
assertEquals(buffer.capacity(), offset);
@@ -207,8 +210,19 @@
buffer.get(magic);
assertArrayEquals(magicBytes(expectedVersion), magic);
+ assertEquals(
+ expectedVersion.isContainerDex()
+ ? Constants.TYPE_HEADER_ITEM_SIZE_V41
+ : Constants.TYPE_HEADER_ITEM_SIZE,
+ buffer.getInt(offset + HEADER_SIZE_OFFSET));
assertEquals(stringIdsSize, buffer.getInt(offset + STRING_IDS_SIZE_OFFSET));
assertEquals(stringIdsOffset, buffer.getInt(offset + STRING_IDS_OFF_OFFSET));
+ if (expectedVersion.isContainerDex()) {
+ assertEquals(0, buffer.getInt(offset + DATA_SIZE_OFFSET));
+ assertEquals(0, buffer.getInt(offset + DATA_OFF_OFFSET));
+ // Additional header field from V41.
+ assertEquals(offset, buffer.getInt(offset + HEADER_OFF_OFFSET));
+ }
assertEquals(stringIdsSize, getSizeFromMap(TYPE_STRING_ID_ITEM, buffer, offset));
assertEquals(stringIdsOffset, getOffsetFromMap(TYPE_STRING_ID_ITEM, buffer, offset));
}