From d7e9884f2f0b275921df0f36b33b91fddeafe1fc Mon Sep 17 00:00:00 2001 From: kgui Date: Tue, 9 Jan 2024 11:56:45 +0800 Subject: Support v41 DEX version for dexdeps. In Android 15 most CTS APKs will be upgraded to using DEX v41 in classes.dex. Bug: 319178625 Test: locally on an APK built internally Change-Id: Ia1346aab3f7d4552c4a3289b5df4161c6f0686c0 --- tools/dexdeps/src/com/android/dexdeps/DexData.java | 276 +++++++++------------ 1 file changed, 122 insertions(+), 154 deletions(-) diff --git a/tools/dexdeps/src/com/android/dexdeps/DexData.java b/tools/dexdeps/src/com/android/dexdeps/DexData.java index 1b6cc98ed..e7f9a53a4 100644 --- a/tools/dexdeps/src/com/android/dexdeps/DexData.java +++ b/tools/dexdeps/src/com/android/dexdeps/DexData.java @@ -23,13 +23,11 @@ import java.nio.ByteOrder; import java.nio.charset.StandardCharsets; import java.util.Arrays; -/** - * Data extracted from a DEX file. - */ +/** Data extracted from a DEX file. */ public class DexData { private RandomAccessFile mDexFile; private HeaderItem mHeaderItem; - private String[] mStrings; // strings from string_data_* + private String[] mStrings; // strings from string_data_* private TypeIdItem[] mTypeIds; private ProtoIdItem[] mProtoIds; private FieldIdItem[] mFieldIds; @@ -39,9 +37,7 @@ public class DexData { private byte tmpBuf[] = new byte[4]; private ByteOrder mByteOrder = ByteOrder.LITTLE_ENDIAN; - /** - * Constructs a new DexData for this file. - */ + /** Constructs a new DexData for this file. */ public DexData(RandomAccessFile raf) { mDexFile = raf; } @@ -65,19 +61,16 @@ public class DexData { markInternalClasses(); } - /** - * Verifies the given magic number. - */ + /** Verifies the given magic number. */ private static boolean verifyMagic(byte[] magic) { - return Arrays.equals(magic, HeaderItem.DEX_FILE_MAGIC_v035) || - Arrays.equals(magic, HeaderItem.DEX_FILE_MAGIC_v037) || - Arrays.equals(magic, HeaderItem.DEX_FILE_MAGIC_v038) || - Arrays.equals(magic, HeaderItem.DEX_FILE_MAGIC_v039); + return Arrays.equals(magic, HeaderItem.DEX_FILE_MAGIC_v035) + || Arrays.equals(magic, HeaderItem.DEX_FILE_MAGIC_v037) + || Arrays.equals(magic, HeaderItem.DEX_FILE_MAGIC_v038) + || Arrays.equals(magic, HeaderItem.DEX_FILE_MAGIC_v039) + || Arrays.equals(magic, HeaderItem.DEX_FILE_MAGIC_v041); } - /** - * Parses the interesting bits out of the header. - */ + /** Parses the interesting bits out of the header. */ void parseHeaderItem() throws IOException { mHeaderItem = new HeaderItem(); @@ -86,8 +79,7 @@ public class DexData { byte[] magic = new byte[8]; readBytes(magic); if (!verifyMagic(magic)) { - System.err.println("Magic number is wrong -- are you sure " + - "this is a DEX file?"); + System.err.println("Magic number is wrong -- are you sure " + "this is a DEX file?"); throw new DexDataException(); } @@ -95,20 +87,21 @@ public class DexData { * Read the endian tag, so we properly swap things as we read * them from here on. */ - seek(8+4+20+4+4); + seek(8 + 4 + 20 + 4 + 4); mHeaderItem.endianTag = readInt(); if (mHeaderItem.endianTag == HeaderItem.ENDIAN_CONSTANT) { /* do nothing */ - } else if (mHeaderItem.endianTag == HeaderItem.REVERSE_ENDIAN_CONSTANT){ + } else if (mHeaderItem.endianTag == HeaderItem.REVERSE_ENDIAN_CONSTANT) { /* file is big-endian (!), reverse future reads */ mByteOrder = ByteOrder.BIG_ENDIAN; } else { - System.err.println("Endian constant has unexpected value " + - Integer.toHexString(mHeaderItem.endianTag)); + System.err.println( + "Endian constant has unexpected value " + + Integer.toHexString(mHeaderItem.endianTag)); throw new DexDataException(); } - seek(8+4+20); // magic, checksum, signature + seek(8 + 4 + 20); // magic, checksum, signature ByteBuffer buffer = readByteBuffer(Integer.BYTES * 20); mHeaderItem.fileSize = buffer.getInt(); mHeaderItem.headerSize = buffer.getInt(); @@ -135,15 +128,14 @@ public class DexData { /** * Loads the string table out of the DEX. * - * First we read all of the string_id_items, then we read all of the - * string_data_item. Doing it this way should allow us to avoid - * seeking around in the file. + *

First we read all of the string_id_items, then we read all of the string_data_item. Doing + * it this way should allow us to avoid seeking around in the file. */ void loadStrings() throws IOException { int count = mHeaderItem.stringIdsSize; int stringOffsets[] = new int[count]; - //System.out.println("reading " + count + " strings"); + // System.out.println("reading " + count + " strings"); seek(mHeaderItem.stringIdsOff); readByteBuffer(Integer.BYTES * count).asIntBuffer().get(stringOffsets); @@ -152,39 +144,35 @@ public class DexData { seek(stringOffsets[0]); for (int i = 0; i < count; i++) { - seek(stringOffsets[i]); // should be a no-op + seek(stringOffsets[i]); // should be a no-op mStrings[i] = readString(); - //System.out.println("STR: " + i + ": " + mStrings[i]); + // System.out.println("STR: " + i + ": " + mStrings[i]); } } - /** - * Loads the type ID list. - */ + /** Loads the type ID list. */ void loadTypeIds() throws IOException { int count = mHeaderItem.typeIdsSize; mTypeIds = new TypeIdItem[count]; - //System.out.println("reading " + count + " typeIds"); + // System.out.println("reading " + count + " typeIds"); seek(mHeaderItem.typeIdsOff); ByteBuffer buffer = readByteBuffer(Integer.BYTES * count); for (int i = 0; i < count; i++) { mTypeIds[i] = new TypeIdItem(); mTypeIds[i].descriptorIdx = buffer.getInt(); - //System.out.println(i + ": " + mTypeIds[i].descriptorIdx + + // System.out.println(i + ": " + mTypeIds[i].descriptorIdx + // " " + mStrings[mTypeIds[i].descriptorIdx]); } } - /** - * Loads the proto ID list. - */ + /** Loads the proto ID list. */ void loadProtoIds() throws IOException { int count = mHeaderItem.protoIdsSize; mProtoIds = new ProtoIdItem[count]; - //System.out.println("reading " + count + " protoIds"); + // System.out.println("reading " + count + " protoIds"); seek(mHeaderItem.protoIdsOff); ByteBuffer buffer = readByteBuffer(Integer.BYTES * 3 * count); @@ -197,7 +185,7 @@ public class DexData { mProtoIds[i].returnTypeIdx = buffer.getInt(); mProtoIds[i].parametersOff = buffer.getInt(); - //System.out.println(i + ": " + mProtoIds[i].shortyIdx + + // System.out.println(i + ": " + mProtoIds[i].shortyIdx + // " " + mStrings[mProtoIds[i].shortyIdx]); } @@ -214,7 +202,7 @@ public class DexData { continue; } else { seek(offset); - int size = readInt(); // #of entries in list + int size = readInt(); // #of entries in list buffer = readByteBuffer(Short.BYTES * size); protoId.types = new int[size]; @@ -225,14 +213,12 @@ public class DexData { } } - /** - * Loads the field ID list. - */ + /** Loads the field ID list. */ void loadFieldIds() throws IOException { int count = mHeaderItem.fieldIdsSize; mFieldIds = new FieldIdItem[count]; - //System.out.println("reading " + count + " fieldIds"); + // System.out.println("reading " + count + " fieldIds"); seek(mHeaderItem.fieldIdsOff); ByteBuffer buffer = readByteBuffer((Integer.BYTES + Short.BYTES * 2) * count); for (int i = 0; i < count; i++) { @@ -241,19 +227,17 @@ public class DexData { mFieldIds[i].typeIdx = buffer.getShort() & 0xffff; mFieldIds[i].nameIdx = buffer.getInt(); - //System.out.println(i + ": " + mFieldIds[i].nameIdx + + // System.out.println(i + ": " + mFieldIds[i].nameIdx + // " " + mStrings[mFieldIds[i].nameIdx]); } } - /** - * Loads the method ID list. - */ + /** Loads the method ID list. */ void loadMethodIds() throws IOException { int count = mHeaderItem.methodIdsSize; mMethodIds = new MethodIdItem[count]; - //System.out.println("reading " + count + " methodIds"); + // System.out.println("reading " + count + " methodIds"); seek(mHeaderItem.methodIdsOff); ByteBuffer buffer = readByteBuffer((Integer.BYTES + Short.BYTES * 2) * count); for (int i = 0; i < count; i++) { @@ -262,44 +246,42 @@ public class DexData { mMethodIds[i].protoIdx = buffer.getShort() & 0xffff; mMethodIds[i].nameIdx = buffer.getInt(); - //System.out.println(i + ": " + mMethodIds[i].nameIdx + + // System.out.println(i + ": " + mMethodIds[i].nameIdx + // " " + mStrings[mMethodIds[i].nameIdx]); } } - /** - * Loads the class defs list. - */ + /** Loads the class defs list. */ void loadClassDefs() throws IOException { int count = mHeaderItem.classDefsSize; mClassDefs = new ClassDefItem[count]; - //System.out.println("reading " + count + " classDefs"); + // System.out.println("reading " + count + " classDefs"); seek(mHeaderItem.classDefsOff); ByteBuffer buffer = readByteBuffer(Integer.BYTES * 8 * count); for (int i = 0; i < count; i++) { mClassDefs[i] = new ClassDefItem(); mClassDefs[i].classIdx = buffer.getInt(); - /* access_flags = */ buffer.getInt(); - /* superclass_idx = */ buffer.getInt(); - /* interfaces_off = */ buffer.getInt(); - /* source_file_idx = */ buffer.getInt(); - /* annotations_off = */ buffer.getInt(); - /* class_data_off = */ buffer.getInt(); - /* static_values_off = */ buffer.getInt(); + /* access_flags= */ buffer.getInt(); + /* superclass_idx= */ buffer.getInt(); + /* interfaces_off= */ buffer.getInt(); + /* source_file_idx= */ buffer.getInt(); + /* annotations_off= */ buffer.getInt(); + /* class_data_off= */ buffer.getInt(); + /* static_values_off= */ buffer.getInt(); - //System.out.println(i + ": " + mClassDefs[i].classIdx + " " + + // System.out.println(i + ": " + mClassDefs[i].classIdx + " " + // mStrings[mTypeIds[mClassDefs[i].classIdx].descriptorIdx]); } } /** - * Sets the "internal" flag on type IDs which are defined in the - * DEX file or within the VM (e.g. primitive classes and arrays). + * Sets the "internal" flag on type IDs which are defined in the DEX file or within the VM (e.g. + * primitive classes and arrays). */ void markInternalClasses() { - for (int i = mClassDefs.length -1; i >= 0; i--) { + for (int i = mClassDefs.length - 1; i >= 0; i--) { mTypeIds[mClassDefs[i].classIdx].internal = true; } @@ -313,29 +295,25 @@ public class DexData { mTypeIds[i].internal = true; } - //System.out.println(i + " " + + // System.out.println(i + " " + // (mTypeIds[i].internal ? "INTERNAL" : "external") + " - " + // mStrings[mTypeIds[i].descriptorIdx]); } } - /* * ======================================================================= * Queries * ======================================================================= */ - /** - * Returns the class name, given an index into the type_ids table. - */ + /** Returns the class name, given an index into the type_ids table. */ private String classNameFromTypeIndex(int idx) { return mStrings[mTypeIds[idx].descriptorIdx]; } /** - * Returns an array of method argument type strings, given an index - * into the proto_ids table. + * Returns an array of method argument type strings, given an index into the proto_ids table. */ private String[] argArrayFromProtoIndex(int idx) { ProtoIdItem protoId = mProtoIds[idx]; @@ -349,8 +327,8 @@ public class DexData { } /** - * Returns a string representing the method's return type, given an - * index into the proto_ids table. + * Returns a string representing the method's return type, given an index into the proto_ids + * table. */ private String returnTypeFromProtoIndex(int idx) { ProtoIdItem protoId = mProtoIds[idx]; @@ -358,9 +336,8 @@ public class DexData { } /** - * Returns an array with all of the class references that don't - * correspond to classes in the DEX file. Each class reference has - * a list of the referenced fields and methods associated with + * Returns an array with all of the class references that don't correspond to classes in the DEX + * file. Each class reference has a list of the referenced fields and methods associated with * that class. */ public ClassRef[] getExternalReferences() { @@ -371,8 +348,7 @@ public class DexData { int count = 0; for (int i = 0; i < mTypeIds.length; i++) { if (!mTypeIds[i].internal) { - sparseRefs[i] = - new ClassRef(mStrings[mTypeIds[i].descriptorIdx]); + sparseRefs[i] = new ClassRef(mStrings[mTypeIds[i].descriptorIdx]); count++; } } @@ -385,8 +361,7 @@ public class DexData { ClassRef[] classRefs = new ClassRef[count]; int idx = 0; for (int i = 0; i < mTypeIds.length; i++) { - if (sparseRefs[i] != null) - classRefs[idx++] = sparseRefs[i]; + if (sparseRefs[i] != null) classRefs[idx++] = sparseRefs[i]; } assert idx == count; @@ -395,87 +370,84 @@ public class DexData { } /** - * Runs through the list of field references, inserting external - * references into the appropriate ClassRef. + * Runs through the list of field references, inserting external references into the appropriate + * ClassRef. */ private void addExternalFieldReferences(ClassRef[] sparseRefs) { for (int i = 0; i < mFieldIds.length; i++) { if (!mTypeIds[mFieldIds[i].classIdx].internal) { FieldIdItem fieldId = mFieldIds[i]; - FieldRef newFieldRef = new FieldRef( - classNameFromTypeIndex(fieldId.classIdx), - classNameFromTypeIndex(fieldId.typeIdx), - mStrings[fieldId.nameIdx]); + FieldRef newFieldRef = + new FieldRef( + classNameFromTypeIndex(fieldId.classIdx), + classNameFromTypeIndex(fieldId.typeIdx), + mStrings[fieldId.nameIdx]); sparseRefs[mFieldIds[i].classIdx].addField(newFieldRef); } } } /** - * Runs through the list of method references, inserting external - * references into the appropriate ClassRef. + * Runs through the list of method references, inserting external references into the + * appropriate ClassRef. */ private void addExternalMethodReferences(ClassRef[] sparseRefs) { for (int i = 0; i < mMethodIds.length; i++) { if (!mTypeIds[mMethodIds[i].classIdx].internal) { MethodIdItem methodId = mMethodIds[i]; - MethodRef newMethodRef = new MethodRef( - classNameFromTypeIndex(methodId.classIdx), - argArrayFromProtoIndex(methodId.protoIdx), - returnTypeFromProtoIndex(methodId.protoIdx), - mStrings[methodId.nameIdx]); + MethodRef newMethodRef = + new MethodRef( + classNameFromTypeIndex(methodId.classIdx), + argArrayFromProtoIndex(methodId.protoIdx), + returnTypeFromProtoIndex(methodId.protoIdx), + mStrings[methodId.nameIdx]); sparseRefs[mMethodIds[i].classIdx].addMethod(newMethodRef); } } } - /* * ======================================================================= * Basic I/O functions * ======================================================================= */ - /** - * Seeks the DEX file to the specified absolute position. - */ + /** Seeks the DEX file to the specified absolute position. */ void seek(int position) throws IOException { mDexFile.seek(position); } - /** - * Fills the buffer by reading bytes from the DEX file. - */ + /** Fills the buffer by reading bytes from the DEX file. */ void readBytes(byte[] buffer) throws IOException { mDexFile.readFully(buffer); } - /** - * Reads a single signed byte value. - */ + /** Reads a single signed byte value. */ byte readByte() throws IOException { mDexFile.readFully(tmpBuf, 0, 1); return tmpBuf[0]; } - /** - * Reads a signed 32-bit integer, byte-swapping if necessary. - */ + /** Reads a signed 32-bit integer, byte-swapping if necessary. */ int readInt() throws IOException { mDexFile.readFully(tmpBuf, 0, 4); if (mByteOrder == ByteOrder.BIG_ENDIAN) { - return (tmpBuf[3] & 0xff) | ((tmpBuf[2] & 0xff) << 8) | - ((tmpBuf[1] & 0xff) << 16) | ((tmpBuf[0] & 0xff) << 24); + return (tmpBuf[3] & 0xff) + | ((tmpBuf[2] & 0xff) << 8) + | ((tmpBuf[1] & 0xff) << 16) + | ((tmpBuf[0] & 0xff) << 24); } else { - return (tmpBuf[0] & 0xff) | ((tmpBuf[1] & 0xff) << 8) | - ((tmpBuf[2] & 0xff) << 16) | ((tmpBuf[3] & 0xff) << 24); + return (tmpBuf[0] & 0xff) + | ((tmpBuf[1] & 0xff) << 8) + | ((tmpBuf[2] & 0xff) << 16) + | ((tmpBuf[3] & 0xff) << 24); } } /** - * Reads a variable-length unsigned LEB128 value. Does not attempt to - * verify that the value is valid. + * Reads a variable-length unsigned LEB128 value. Does not attempt to verify that the value is + * valid. * * @throws EOFException if we run off the end of the file */ @@ -504,14 +476,15 @@ public class DexData { /** * Reads a UTF-8 string. * - * We don't know how long the UTF-8 string is, so we try to read the worst case amount of bytes. + *

We don't know how long the UTF-8 string is, so we try to read the worst case amount of + * bytes. * - * Note that the dex file pointer will likely be at a wrong location after this operation, which - * means it can't be used in the middle of sequential reads. + *

Note that the dex file pointer will likely be at a wrong location after this operation, + * which means it can't be used in the middle of sequential reads. */ String readString() throws IOException { int utf16len = readUnsignedLeb128(); - byte inBuf[] = new byte[utf16len * 3]; // worst case + byte inBuf[] = new byte[utf16len * 3]; // worst case int bytesRead = mDexFile.read(inBuf); for (int i = 0; i < bytesRead; i++) { @@ -530,9 +503,7 @@ public class DexData { * ======================================================================= */ - /** - * Holds the contents of a header_item. - */ + /** Holds the contents of a header_item. */ static class HeaderItem { public int fileSize; public int headerSize; @@ -546,7 +517,7 @@ public class DexData { /* expected magic values */ public static final byte[] DEX_FILE_MAGIC_v035 = - "dex\n035\0".getBytes(StandardCharsets.US_ASCII); + "dex\n035\0".getBytes(StandardCharsets.US_ASCII); // Dex version 036 skipped because of an old dalvik bug on some versions // of android where dex files with that version number would erroneously @@ -554,15 +525,19 @@ public class DexData { // V037 was introduced in API LEVEL 24 public static final byte[] DEX_FILE_MAGIC_v037 = - "dex\n037\0".getBytes(StandardCharsets.US_ASCII); + "dex\n037\0".getBytes(StandardCharsets.US_ASCII); // V038 was introduced in API LEVEL 26 public static final byte[] DEX_FILE_MAGIC_v038 = - "dex\n038\0".getBytes(StandardCharsets.US_ASCII); + "dex\n038\0".getBytes(StandardCharsets.US_ASCII); // V039 was introduced in API LEVEL 28 public static final byte[] DEX_FILE_MAGIC_v039 = - "dex\n039\0".getBytes(StandardCharsets.US_ASCII); + "dex\n039\0".getBytes(StandardCharsets.US_ASCII); + + // V041 will be introduced in API LEVEL 35 + public static final byte[] DEX_FILE_MAGIC_v041 = + "dex\n041\0".getBytes(StandardCharsets.US_ASCII); public static final int ENDIAN_CONSTANT = 0x12345678; public static final int REVERSE_ENDIAN_CONSTANT = 0x78563412; @@ -571,54 +546,47 @@ public class DexData { /** * Holds the contents of a type_id_item. * - * This is chiefly a list of indices into the string table. We need - * some additional bits of data, such as whether or not the type ID - * represents a class defined in this DEX, so we use an object for - * each instead of a simple integer. (Could use a parallel array, but - * since this is a desktop app it's not essential.) + *

This is chiefly a list of indices into the string table. We need some additional bits of + * data, such as whether or not the type ID represents a class defined in this DEX, so we use an + * object for each instead of a simple integer. (Could use a parallel array, but since this is a + * desktop app it's not essential.) */ static class TypeIdItem { - public int descriptorIdx; // index into string_ids + public int descriptorIdx; // index into string_ids - public boolean internal; // defined within this DEX file? + public boolean internal; // defined within this DEX file? } - /** - * Holds the contents of a proto_id_item. - */ + /** Holds the contents of a proto_id_item. */ static class ProtoIdItem { - public int shortyIdx; // index into string_ids - public int returnTypeIdx; // index into type_ids - public int parametersOff; // file offset to a type_list + public int shortyIdx; // index into string_ids + public int returnTypeIdx; // index into type_ids + public int parametersOff; // file offset to a type_list - public int types[]; // contents of type list + public int types[]; // contents of type list } - /** - * Holds the contents of a field_id_item. - */ + /** Holds the contents of a field_id_item. */ static class FieldIdItem { - public int classIdx; // index into type_ids (defining class) - public int typeIdx; // index into type_ids (field type) - public int nameIdx; // index into string_ids + public int classIdx; // index into type_ids (defining class) + public int typeIdx; // index into type_ids (field type) + public int nameIdx; // index into string_ids } - /** - * Holds the contents of a method_id_item. - */ + /** Holds the contents of a method_id_item. */ static class MethodIdItem { - public int classIdx; // index into type_ids - public int protoIdx; // index into proto_ids - public int nameIdx; // index into string_ids + public int classIdx; // index into type_ids + public int protoIdx; // index into proto_ids + public int nameIdx; // index into string_ids } /** * Holds the contents of a class_def_item. * - * We don't really need a class for this, but there's some stuff in - * the class_def_item that we might want later. + *

We don't really need a class for this, but there's some stuff in the class_def_item that + * we might want later. */ static class ClassDefItem { - public int classIdx; // index into type_ids + public int classIdx; // index into type_ids } } -- cgit v1.2.3