Take into account defining class information when resolving field offsets

This fixes an issue when deodexing, where a field offset is incorrectly
resolved when trying to access a field defined in an ancestor class
when the the child class defines a field with the same name

Kudos to brightidea @ xda-dev for digging into this issue and figuring
out the problem!
This commit is contained in:
Ben Gruver 2011-08-12 23:15:12 -04:00
parent c2e3d1a320
commit a91109b8a7
2 changed files with 45 additions and 35 deletions

View File

@ -566,6 +566,18 @@ public class ClassPath {
} }
} }
public static class FieldDef {
public final String definingClass;
public final String name;
public final String type;
public FieldDef(String definingClass, String name, String type) {
this.definingClass = definingClass;
this.name = name;
this.type = type;
}
}
public static class ClassDef implements Comparable<ClassDef> { public static class ClassDef implements Comparable<ClassDef> {
private final String classType; private final String classType;
private final ClassDef superclass; private final ClassDef superclass;
@ -583,7 +595,7 @@ public class ClassPath {
private final String[] vtable; private final String[] vtable;
private final HashMap<String, Integer> virtualMethodLookup; private final HashMap<String, Integer> virtualMethodLookup;
private final SparseArray<String> instanceFields; private final SparseArray<FieldDef> instanceFields;
public final static int ArrayClassDef = 0; public final static int ArrayClassDef = 0;
public final static int PrimitiveClassDef = 1; public final static int PrimitiveClassDef = 1;
@ -736,7 +748,7 @@ public class ClassPath {
return virtualMethodLookup.containsKey(method); return virtualMethodLookup.containsKey(method);
} }
public String getInstanceField(int fieldOffset) { public FieldDef getInstanceField(int fieldOffset) {
return this.instanceFields.get(fieldOffset, null); return this.instanceFields.get(fieldOffset, null);
} }
@ -747,12 +759,12 @@ public class ClassPath {
return this.vtable[vtableIndex]; return this.vtable[vtableIndex];
} }
private void swap(byte[] fieldTypes, String[] fields, int position1, int position2) { private void swap(byte[] fieldTypes, FieldDef[] fields, int position1, int position2) {
byte tempType = fieldTypes[position1]; byte tempType = fieldTypes[position1];
fieldTypes[position1] = fieldTypes[position2]; fieldTypes[position1] = fieldTypes[position2];
fieldTypes[position2] = tempType; fieldTypes[position2] = tempType;
String tempField = fields[position1]; FieldDef tempField = fields[position1];
fields[position1] = fields[position2]; fields[position1] = fields[position2];
fields[position2] = tempField; fields[position2] = tempField;
} }
@ -915,11 +927,9 @@ public class ClassPath {
int lastItemIndex = instanceFields.size()-1; int lastItemIndex = instanceFields.size()-1;
int fieldOffset = instanceFields.keyAt(lastItemIndex); int fieldOffset = instanceFields.keyAt(lastItemIndex);
String lastField = instanceFields.valueAt(lastItemIndex); FieldDef lastField = instanceFields.valueAt(lastItemIndex);
int fieldTypeIndex = lastField.indexOf(":") + 1; switch (lastField.type.charAt(0)) {
switch (lastField.charAt(fieldTypeIndex)) {
case 'J': case 'J':
case 'D': case 'D':
return fieldOffset + 8; return fieldOffset + 8;
@ -928,7 +938,7 @@ public class ClassPath {
} }
} }
private SparseArray<String> loadFields(TempClassInfo classInfo) { private SparseArray<FieldDef> loadFields(TempClassInfo classInfo) {
//This is a bit of an "involved" operation. We need to follow the same algorithm that dalvik uses to //This is a bit of an "involved" operation. We need to follow the same algorithm that dalvik uses to
//arrange fields, so that we end up with the same field offsets (which is needed for deodexing). //arrange fields, so that we end up with the same field offsets (which is needed for deodexing).
//See mydroid/dalvik/vm/oo/Class.c - computeFieldOffsets() //See mydroid/dalvik/vm/oo/Class.c - computeFieldOffsets()
@ -937,12 +947,12 @@ public class ClassPath {
final byte WIDE = 1; final byte WIDE = 1;
final byte OTHER = 2; final byte OTHER = 2;
String[] fields = null; FieldDef[] fields = null;
//the "type" for each field in fields. 0=reference,1=wide,2=other //the "type" for each field in fields. 0=reference,1=wide,2=other
byte[] fieldTypes = null; byte[] fieldTypes = null;
if (classInfo.instanceFields != null) { if (classInfo.instanceFields != null) {
fields = new String[classInfo.instanceFields.length]; fields = new FieldDef[classInfo.instanceFields.length];
fieldTypes = new byte[fields.length]; fieldTypes = new byte[fields.length];
for (int i=0; i<fields.length; i++) { for (int i=0; i<fields.length; i++) {
@ -951,14 +961,13 @@ public class ClassPath {
String fieldName = fieldInfo[0]; String fieldName = fieldInfo[0];
String fieldType = fieldInfo[1]; String fieldType = fieldInfo[1];
String field = String.format("%s:%s", fieldName, fieldType);
fieldTypes[i] = getFieldType(fieldType); fieldTypes[i] = getFieldType(fieldType);
fields[i] = field; fields[i] = new FieldDef(classInfo.classType, fieldName, fieldType);
} }
} }
if (fields == null) { if (fields == null) {
fields = new String[0]; fields = new FieldDef[0];
fieldTypes = new byte[0]; fieldTypes = new byte[0];
} }
@ -1041,7 +1050,7 @@ public class ClassPath {
//now the fields are in the correct order. Add them to the SparseArray and lookup, and calculate the offsets //now the fields are in the correct order. Add them to the SparseArray and lookup, and calculate the offsets
int totalFieldCount = superFieldCount + fields.length; int totalFieldCount = superFieldCount + fields.length;
SparseArray<String> instanceFields = new SparseArray<String>(totalFieldCount); SparseArray<FieldDef> instanceFields = new SparseArray<FieldDef>(totalFieldCount);
int fieldOffset; int fieldOffset;
@ -1052,10 +1061,8 @@ public class ClassPath {
fieldOffset = instanceFields.keyAt(superFieldCount-1); fieldOffset = instanceFields.keyAt(superFieldCount-1);
String lastSuperField = superclass.instanceFields.valueAt(superFieldCount-1); FieldDef lastSuperField = superclass.instanceFields.valueAt(superFieldCount-1);
assert lastSuperField.indexOf(':') >= 0; char fieldType = lastSuperField.type.charAt(0);
assert lastSuperField.indexOf(':') < lastSuperField.length()-1; //the ':' shouldn't be the last char
char fieldType = lastSuperField.charAt(lastSuperField.indexOf(':') + 1);
if (fieldType == 'J' || fieldType == 'D') { if (fieldType == 'J' || fieldType == 'D') {
fieldOffset += 8; fieldOffset += 8;
} else { } else {
@ -1068,7 +1075,7 @@ public class ClassPath {
boolean gotDouble = false; boolean gotDouble = false;
for (int i=0; i<fields.length; i++) { for (int i=0; i<fields.length; i++) {
String field = fields[i]; FieldDef field = fields[i];
//add padding to align the wide fields, if needed //add padding to align the wide fields, if needed
if (fieldTypes[i] == WIDE && !gotDouble) { if (fieldTypes[i] == WIDE && !gotDouble) {

View File

@ -30,6 +30,7 @@ package org.jf.dexlib.Code.Analysis;
import org.jf.dexlib.*; import org.jf.dexlib.*;
import java.util.ArrayList;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -59,7 +60,7 @@ public class DeodexUtil {
} }
public FieldIdItem lookupField(ClassPath.ClassDef classDef, int fieldOffset) { public FieldIdItem lookupField(ClassPath.ClassDef classDef, int fieldOffset) {
String field = classDef.getInstanceField(fieldOffset); ClassPath.FieldDef field = classDef.getInstanceField(fieldOffset);
if (field == null) { if (field == null) {
return null; return null;
} }
@ -204,15 +205,10 @@ public class DeodexUtil {
return null; return null;
} }
private FieldIdItem parseAndResolveField(ClassPath.ClassDef classDef, String field) { private FieldIdItem parseAndResolveField(ClassPath.ClassDef classDef, ClassPath.FieldDef field) {
//expecting a string like someField:Lfield/type; String definingClass = field.definingClass;
String[] parts = field.split(":"); String fieldName = field.name;
if (parts.length != 2) { String fieldType = field.type;
throw new RuntimeException("Invalid field descriptor " + field);
}
String fieldName = parts[0];
String fieldType = parts[1];
StringIdItem fieldNameItem = StringIdItem.lookupStringIdItem(dexFile, fieldName); StringIdItem fieldNameItem = StringIdItem.lookupStringIdItem(dexFile, fieldName);
if (fieldNameItem == null) { if (fieldNameItem == null) {
@ -226,7 +222,17 @@ public class DeodexUtil {
ClassPath.ClassDef fieldClass = classDef; ClassPath.ClassDef fieldClass = classDef;
do { ArrayList<ClassPath.ClassDef> parents = new ArrayList<ClassPath.ClassDef>();
parents.add(fieldClass);
while (fieldClass != null && !fieldClass.getClassType().equals(definingClass)) {
fieldClass = fieldClass.getSuperclass();
parents.add(fieldClass);
}
for (int i=parents.size()-1; i>=0; i--) {
fieldClass = parents.get(i);
TypeIdItem classTypeItem = TypeIdItem.lookupTypeIdItem(dexFile, fieldClass.getClassType()); TypeIdItem classTypeItem = TypeIdItem.lookupTypeIdItem(dexFile, fieldClass.getClassType());
if (classTypeItem == null) { if (classTypeItem == null) {
continue; continue;
@ -236,10 +242,7 @@ public class DeodexUtil {
if (fieldIdItem != null) { if (fieldIdItem != null) {
return fieldIdItem; return fieldIdItem;
} }
}
fieldClass = fieldClass.getSuperclass();
} while (fieldClass != null);
return null; return null;
} }