Add support for calculating field offsets for art

This commit is contained in:
Ben Gruver 2015-07-26 18:44:53 -07:00
parent 64785d79a3
commit c8c70ac58e
4 changed files with 368 additions and 210 deletions

View File

@ -31,6 +31,8 @@
package org.jf.dexlib2.analysis;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
@ -205,7 +207,7 @@ public class ClassPath {
dexFiles.addAll(classPathDexFiles);
}
dexFiles.add(dexFile);
return new ClassPath(dexFiles, checkPackagePrivateAccess);
return new ClassPath(dexFiles, checkPackagePrivateAccess, isArt);
}
@Nonnull
@ -215,9 +217,7 @@ public class ClassPath {
ArrayList<DexFile> dexFiles = Lists.newArrayList();
for (String classPathEntry: classPath) {
try {
dexFiles.addAll(loadClassPathEntry(classPathDirs, classPathEntry, api, experimental));
} catch (ExceptionWithContext e){}
dexFiles.addAll(loadClassPathEntry(classPathDirs, classPathEntry, api, experimental));
}
dexFiles.add(dexFile);
return new ClassPath(dexFiles, checkPackagePrivateAccess, isArt);
@ -286,4 +286,16 @@ public class ClassPath {
}
throw new ExceptionWithContext("Cannot locate boot class path file %s", bootClassPathEntry);
}
private final Supplier<OdexedFieldInstructionMapper> fieldInstructionMapperSupplier = Suppliers.memoize(
new Supplier<OdexedFieldInstructionMapper>() {
@Override public OdexedFieldInstructionMapper get() {
return new OdexedFieldInstructionMapper(isArt);
}
});
@Nonnull
public OdexedFieldInstructionMapper getFieldInstructionMapper() {
return fieldInstructionMapperSupplier.get();
}
}

View File

@ -34,10 +34,8 @@ package org.jf.dexlib2.analysis;
import com.google.common.base.Predicates;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.*;
import com.google.common.primitives.Ints;
import org.jf.dexlib2.AccessFlags;
import org.jf.dexlib2.analysis.util.TypeProtoUtils;
import org.jf.dexlib2.iface.ClassDef;
@ -46,21 +44,23 @@ import org.jf.dexlib2.iface.Method;
import org.jf.dexlib2.iface.reference.FieldReference;
import org.jf.dexlib2.iface.reference.MethodReference;
import org.jf.dexlib2.immutable.ImmutableMethod;
import org.jf.util.AlignmentUtils;
import org.jf.util.ExceptionWithContext;
import org.jf.util.SparseArray;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.*;
/**
* A class "prototype". This contains things like the interfaces, the superclass, the vtable and the instance fields
* and their offsets.
*/
public class ClassProto implements TypeProto {
private static final byte REFERENCE = 0;
private static final byte WIDE = 1;
private static final byte OTHER = 2;
@Nonnull protected final ClassPath classPath;
@Nonnull protected final String type;
@ -356,20 +356,20 @@ public class ClassProto implements TypeProto {
}
@Nonnull SparseArray<FieldReference> getInstanceFields() {
return instanceFieldsSupplier.get();
if (classPath.isArt) {
return artInstanceFieldsSupplier.get();
} else {
return dalvikInstanceFieldsSupplier.get();
}
}
@Nonnull private final Supplier<SparseArray<FieldReference>> instanceFieldsSupplier =
@Nonnull private final Supplier<SparseArray<FieldReference>> dalvikInstanceFieldsSupplier =
Suppliers.memoize(new Supplier<SparseArray<FieldReference>>() {
@Override public SparseArray<FieldReference> get() {
//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).
//See mydroid/dalvik/vm/oo/Class.c - computeFieldOffsets()
final byte REFERENCE = 0;
final byte WIDE = 1;
final byte OTHER = 2;
ArrayList<Field> fields = getSortedInstanceFields(getClassDef());
final int fieldCount = fields.size();
//the "type" for each field in fields. 0=reference,1=wide,2=other
@ -520,19 +520,6 @@ public class ClassProto implements TypeProto {
return fields;
}
private byte getFieldType(@Nonnull FieldReference field) {
switch (field.getType().charAt(0)) {
case '[':
case 'L':
return 0; //REFERENCE
case 'J':
case 'D':
return 1; //WIDE
default:
return 2; //OTHER
}
}
private void swap(byte[] fieldTypes, List<Field> fields, int position1, int position2) {
byte tempType = fieldTypes[position1];
fieldTypes[position1] = fieldTypes[position2];
@ -543,23 +530,199 @@ public class ClassProto implements TypeProto {
}
});
private static class FieldGap implements Comparable<FieldGap> {
public final int offset;
public final int size;
public FieldGap(int offset, int size) {
this.offset = offset;
this.size = size;
}
@Override public int compareTo(@Nonnull FieldGap o) {
int result = Ints.compare(o.size, size);
if (result != 0) {
return result;
}
return Ints.compare(o.offset, offset);
}
}
@Nonnull private final Supplier<SparseArray<FieldReference>> artInstanceFieldsSupplier =
Suppliers.memoize(new Supplier<SparseArray<FieldReference>>() {
@Override public SparseArray<FieldReference> get() {
// We need to follow the same algorithm that art uses to arrange fields, so that we end up with the
// same field offsets, which is needed for deodexing.
// See LinkFields() in art/runtime/class_linker.cc
PriorityQueue<FieldGap> gaps = new PriorityQueue<FieldGap>();
SparseArray<FieldReference> linkedFields = new SparseArray<FieldReference>();
ArrayList<Field> fields = getSortedInstanceFields(getClassDef());
int fieldOffset = 0;
String superclassType = getSuperclass();
if (superclassType != null) {
// TODO: what to do if superclass doesn't exist?
ClassProto superclass = (ClassProto) classPath.getClass(superclassType);
SparseArray<FieldReference> superFields = superclass.getInstanceFields();
FieldReference field = null;
int lastOffset = 0;
for (int i=0; i<superFields.size(); i++) {
int offset = superFields.keyAt(i);
field = superFields.valueAt(i);
linkedFields.put(offset, field);
lastOffset = offset;
}
if (field != null) {
fieldOffset = lastOffset + getFieldSize(field);
}
}
for (Field field: fields) {
int fieldSize = getFieldSize(field);
if (!AlignmentUtils.isAligned(fieldOffset, fieldSize)) {
int oldOffset = fieldOffset;
fieldOffset = AlignmentUtils.alignOffset(fieldOffset, fieldSize);
addFieldGap(oldOffset, fieldOffset, gaps);
}
FieldGap gap = gaps.peek();
if (gap != null && gap.size >= fieldSize) {
gaps.poll();
linkedFields.put(gap.offset, field);
if (gap.size > fieldSize) {
addFieldGap(gap.offset + fieldSize, gap.offset + gap.size, gaps);
}
} else {
linkedFields.append(fieldOffset, field);
fieldOffset += fieldSize;
}
}
return linkedFields;
}
private void addFieldGap(int gapStart, int gapEnd, @Nonnull PriorityQueue<FieldGap> gaps) {
int offset = gapStart;
while (offset < gapEnd) {
int remaining = gapEnd - offset;
if ((remaining >= 4) && (offset % 4 == 0)) {
gaps.add(new FieldGap(offset, 4));
offset += 4;
} else if (remaining >= 2 && (offset % 2 == 0)) {
gaps.add(new FieldGap(offset, 2));
offset += 2;
} else {
gaps.add(new FieldGap(offset, 1));
offset += 1;
}
}
}
@Nonnull
private ArrayList<Field> getSortedInstanceFields(@Nonnull ClassDef classDef) {
ArrayList<Field> fields = Lists.newArrayList(classDef.getInstanceFields());
Collections.sort(fields, new Comparator<Field>() {
@Override public int compare(Field field1, Field field2) {
int result = Ints.compare(getFieldSortOrder(field1), getFieldSortOrder(field2));
if (result != 0) {
return result;
}
result = field1.getName().compareTo(field2.getName());
if (result != 0) {
return result;
}
return field1.getType().compareTo(field2.getType());
}
});
return fields;
}
private int getFieldSortOrder(@Nonnull FieldReference field) {
// The sort order is based on type size (except references are first), and then based on the
// enum value of the primitive type for types of equal size. See: Primitive::Type enum
// in art/runtime/primitive.h
switch (field.getType().charAt(0)) {
/* reference */
case '[':
case 'L':
return 0;
/* 64 bit */
case 'J':
return 1;
case 'D':
return 2;
/* 32 bit */
case 'I':
return 3;
case 'F':
return 4;
/* 16 bit */
case 'C':
return 5;
case 'S':
return 6;
/* 8 bit */
case 'Z':
return 7;
case 'B':
return 8;
}
throw new ExceptionWithContext("Invalid field type: %s", field.getType());
}
private int getFieldSize(@Nonnull FieldReference field) {
return getTypeSize(field.getType().charAt(0));
}
});
private int getNextFieldOffset() {
SparseArray<FieldReference> instanceFields = getInstanceFields();
if (instanceFields.size() == 0) {
return 8;
return classPath.isArt ? 0 : 8;
}
int lastItemIndex = instanceFields.size()-1;
int fieldOffset = instanceFields.keyAt(lastItemIndex);
FieldReference lastField = instanceFields.valueAt(lastItemIndex);
switch (lastField.getType().charAt(0)) {
if (classPath.isArt) {
return fieldOffset + getTypeSize(lastField.getType().charAt(0));
} else {
switch (lastField.getType().charAt(0)) {
case 'J':
case 'D':
return fieldOffset + 8;
default:
return fieldOffset + 4;
}
}
}
private static int getTypeSize(char type) {
switch (type) {
case 'J':
case 'D':
return fieldOffset + 8;
default:
return fieldOffset + 4;
return 8;
case '[':
case 'L':
case 'I':
case 'F':
return 4;
case 'C':
case 'S':
return 2;
case 'B':
case 'Z':
return 1;
}
throw new ExceptionWithContext("Invalid type: %s", type);
}
@Nonnull List<Method> getVtable() {
@ -672,4 +835,17 @@ public class ClassProto implements TypeProto {
AccessFlags.PUBLIC.getValue())) == 0;
}
});
private static byte getFieldType(@Nonnull FieldReference field) {
switch (field.getType().charAt(0)) {
case '[':
case 'L':
return 0; //REFERENCE
case 'J':
case 'D':
return 1; //WIDE
default:
return 2; //OTHER
}
}
}

View File

@ -1573,8 +1573,8 @@ public class MethodAnalyzer {
String fieldType = resolvedField.getType();
Opcode opcode = OdexedFieldInstructionMapper.getAndCheckDeodexedOpcodeForOdexedOpcode(fieldType,
instruction.getOpcode());
Opcode opcode = classPath.getFieldInstructionMapper().getAndCheckDeodexedOpcode(
fieldType, instruction.getOpcode());
Instruction22c deodexedInstruction = new ImmutableInstruction22c(opcode, (byte)instruction.getRegisterA(),
(byte)instruction.getRegisterB(), resolvedField);
@ -1726,8 +1726,8 @@ public class MethodAnalyzer {
Opcode originalOpcode = analyzedInstruction.instruction.getOpcode();
Opcode opcode = OdexedFieldInstructionMapper.getAndCheckDeodexedOpcodeForOdexedOpcode(fieldType,
originalOpcode);
Opcode opcode = classPath.getFieldInstructionMapper().getAndCheckDeodexedOpcode(
fieldType, originalOpcode);
Instruction deodexedInstruction;

View File

@ -34,155 +34,111 @@ package org.jf.dexlib2.analysis;
import org.jf.dexlib2.Opcode;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.HashMap;
import java.util.Map;
public class OdexedFieldInstructionMapper {
private static Opcode[][][][] opcodeMap = new Opcode[][][][] {
//get opcodes
new Opcode[][][] {
//iget quick
new Opcode[][] {
//odexed
new Opcode[] {
/*Z*/ Opcode.IGET_QUICK,
/*B*/ Opcode.IGET_QUICK,
/*S*/ Opcode.IGET_QUICK,
/*C*/ Opcode.IGET_QUICK,
/*I,F*/ Opcode.IGET_QUICK,
/*J,D*/ Opcode.IGET_WIDE_QUICK,
/*L,[*/ Opcode.IGET_OBJECT_QUICK
},
//deodexed
new Opcode[] {
/*Z*/ Opcode.IGET_BOOLEAN,
/*B*/ Opcode.IGET_BYTE,
/*S*/ Opcode.IGET_SHORT,
/*C*/ Opcode.IGET_CHAR,
/*I,F*/ Opcode.IGET,
/*J,D*/ Opcode.IGET_WIDE,
/*L,[*/ Opcode.IGET_OBJECT
}
},
//iget volatile
new Opcode[][] {
//odexed
new Opcode[] {
/*Z*/ Opcode.IGET_VOLATILE,
/*B*/ Opcode.IGET_VOLATILE,
/*S*/ Opcode.IGET_VOLATILE,
/*C*/ Opcode.IGET_VOLATILE,
/*I,F*/ Opcode.IGET_VOLATILE,
/*J,D*/ Opcode.IGET_WIDE_VOLATILE,
/*L,[*/ Opcode.IGET_OBJECT_VOLATILE
},
//deodexed
new Opcode[] {
/*Z*/ Opcode.IGET_BOOLEAN,
/*B*/ Opcode.IGET_BYTE,
/*S*/ Opcode.IGET_SHORT,
/*C*/ Opcode.IGET_CHAR,
/*I,F*/ Opcode.IGET,
/*J,D*/ Opcode.IGET_WIDE,
/*L,[*/ Opcode.IGET_OBJECT
}
},
//sget volatile
new Opcode[][] {
//odexed
new Opcode[] {
/*Z*/ Opcode.SGET_VOLATILE,
/*B*/ Opcode.SGET_VOLATILE,
/*S*/ Opcode.SGET_VOLATILE,
/*C*/ Opcode.SGET_VOLATILE,
/*I,F*/ Opcode.SGET_VOLATILE,
/*J,D*/ Opcode.SGET_WIDE_VOLATILE,
/*L,[*/ Opcode.SGET_OBJECT_VOLATILE
},
//deodexed
new Opcode[] {
/*Z*/ Opcode.SGET_BOOLEAN,
/*B*/ Opcode.SGET_BYTE,
/*S*/ Opcode.SGET_SHORT,
/*C*/ Opcode.SGET_CHAR,
/*I,F*/ Opcode.SGET,
/*J,D*/ Opcode.SGET_WIDE,
/*L,[*/ Opcode.SGET_OBJECT
}
}
},
//put opcodes
new Opcode[][][] {
//iput quick
new Opcode[][] {
//odexed
new Opcode[] {
/*Z*/ Opcode.IPUT_QUICK,
/*B*/ Opcode.IPUT_QUICK,
/*S*/ Opcode.IPUT_QUICK,
/*C*/ Opcode.IPUT_QUICK,
/*I,F*/ Opcode.IPUT_QUICK,
/*J,D*/ Opcode.IPUT_WIDE_QUICK,
/*L,[*/ Opcode.IPUT_OBJECT_QUICK
},
//deodexed
new Opcode[] {
/*Z*/ Opcode.IPUT_BOOLEAN,
/*B*/ Opcode.IPUT_BYTE,
/*S*/ Opcode.IPUT_SHORT,
/*C*/ Opcode.IPUT_CHAR,
/*I,F*/ Opcode.IPUT,
/*J,D*/ Opcode.IPUT_WIDE,
/*L,[*/ Opcode.IPUT_OBJECT
}
},
//iput volatile
new Opcode[][] {
//odexed
new Opcode[] {
/*Z*/ Opcode.IPUT_VOLATILE,
/*B*/ Opcode.IPUT_VOLATILE,
/*S*/ Opcode.IPUT_VOLATILE,
/*C*/ Opcode.IPUT_VOLATILE,
/*I,F*/ Opcode.IPUT_VOLATILE,
/*J,D*/ Opcode.IPUT_WIDE_VOLATILE,
/*L,[*/ Opcode.IPUT_OBJECT_VOLATILE
},
//deodexed
new Opcode[] {
/*Z*/ Opcode.IPUT_BOOLEAN,
/*B*/ Opcode.IPUT_BYTE,
/*S*/ Opcode.IPUT_SHORT,
/*C*/ Opcode.IPUT_CHAR,
/*I,F*/ Opcode.IPUT,
/*J,D*/ Opcode.IPUT_WIDE,
/*L,[*/ Opcode.IPUT_OBJECT
}
},
//sput volatile
new Opcode[][] {
//odexed
new Opcode[] {
/*Z*/ Opcode.SPUT_VOLATILE,
/*B*/ Opcode.SPUT_VOLATILE,
/*S*/ Opcode.SPUT_VOLATILE,
/*C*/ Opcode.SPUT_VOLATILE,
/*I,F*/ Opcode.SPUT_VOLATILE,
/*J,D*/ Opcode.SPUT_WIDE_VOLATILE,
/*L,[*/ Opcode.SPUT_OBJECT_VOLATILE
},
//deodexed
new Opcode[] {
/*Z*/ Opcode.SPUT_BOOLEAN,
/*B*/ Opcode.SPUT_BYTE,
/*S*/ Opcode.SPUT_SHORT,
/*C*/ Opcode.SPUT_CHAR,
/*I,F*/ Opcode.SPUT,
/*J,D*/ Opcode.SPUT_WIDE,
/*L,[*/ Opcode.SPUT_OBJECT
}
}
}
private static final int GET = 0;
private static final int PUT = 1;
private static final int PRIMITIVE = 0;
private static final int WIDE = 1;
private static final int REFERENCE = 2;
private static class FieldOpcode {
public final char type;
@Nonnull public final Opcode normalOpcode;
@Nonnull public final Opcode quickOpcode;
@Nullable public final Opcode volatileOpcode;
public FieldOpcode(char type, @Nonnull Opcode normalOpcode, @Nonnull Opcode quickOpcode,
@Nullable Opcode volatileOpcode) {
this.type = type;
this.normalOpcode = normalOpcode;
this.quickOpcode = quickOpcode;
this.volatileOpcode = volatileOpcode;
}
public FieldOpcode(char type, @Nonnull Opcode normalOpcode, @Nonnull Opcode quickOpcode) {
this.type = type;
this.normalOpcode = normalOpcode;
this.quickOpcode = quickOpcode;
this.volatileOpcode = null;
}
}
private static final FieldOpcode[] dalvikFieldOpcodes = new FieldOpcode[] {
new FieldOpcode('Z', Opcode.IGET_BOOLEAN, Opcode.IGET_QUICK, Opcode.IGET_VOLATILE),
new FieldOpcode('B', Opcode.IGET_BYTE, Opcode.IGET_QUICK, Opcode.IGET_VOLATILE),
new FieldOpcode('S', Opcode.IGET_SHORT, Opcode.IGET_QUICK, Opcode.IGET_VOLATILE),
new FieldOpcode('C', Opcode.IGET_CHAR, Opcode.IGET_QUICK, Opcode.IGET_VOLATILE),
new FieldOpcode('I', Opcode.IGET, Opcode.IGET_QUICK, Opcode.IGET_VOLATILE),
new FieldOpcode('F', Opcode.IGET, Opcode.IGET_QUICK, Opcode.IGET_VOLATILE),
new FieldOpcode('J', Opcode.IGET_WIDE, Opcode.IGET_WIDE_QUICK, Opcode.IGET_WIDE_VOLATILE),
new FieldOpcode('D', Opcode.IGET_WIDE, Opcode.IGET_WIDE_QUICK, Opcode.IGET_WIDE_VOLATILE),
new FieldOpcode('L', Opcode.IGET_OBJECT, Opcode.IGET_OBJECT_QUICK, Opcode.IGET_OBJECT_VOLATILE),
new FieldOpcode('[', Opcode.IGET_OBJECT, Opcode.IGET_OBJECT_QUICK, Opcode.IGET_OBJECT_VOLATILE),
new FieldOpcode('Z', Opcode.IPUT_BOOLEAN, Opcode.IPUT_QUICK, Opcode.IPUT_VOLATILE),
new FieldOpcode('B', Opcode.IPUT_BYTE, Opcode.IPUT_QUICK, Opcode.IPUT_VOLATILE),
new FieldOpcode('S', Opcode.IPUT_SHORT, Opcode.IPUT_QUICK, Opcode.IPUT_VOLATILE),
new FieldOpcode('C', Opcode.IPUT_CHAR, Opcode.IPUT_QUICK, Opcode.IPUT_VOLATILE),
new FieldOpcode('I', Opcode.IPUT, Opcode.IPUT_QUICK, Opcode.IPUT_VOLATILE),
new FieldOpcode('F', Opcode.IPUT, Opcode.IPUT_QUICK, Opcode.IPUT_VOLATILE),
new FieldOpcode('J', Opcode.IPUT_WIDE, Opcode.IPUT_WIDE_QUICK, Opcode.IPUT_WIDE_VOLATILE),
new FieldOpcode('D', Opcode.IPUT_WIDE, Opcode.IPUT_WIDE_QUICK, Opcode.IPUT_WIDE_VOLATILE),
new FieldOpcode('L', Opcode.IPUT_OBJECT, Opcode.IPUT_OBJECT_QUICK, Opcode.IPUT_OBJECT_VOLATILE),
new FieldOpcode('[', Opcode.IPUT_OBJECT, Opcode.IPUT_OBJECT_QUICK, Opcode.IPUT_OBJECT_VOLATILE),
};
private static final FieldOpcode[] artFieldOpcodes = new FieldOpcode[] {
new FieldOpcode('Z', Opcode.IGET_BOOLEAN, Opcode.IGET_BOOLEAN_QUICK),
new FieldOpcode('B', Opcode.IGET_BYTE, Opcode.IGET_BYTE_QUICK),
new FieldOpcode('S', Opcode.IGET_SHORT, Opcode.IGET_SHORT_QUICK),
new FieldOpcode('C', Opcode.IGET_CHAR, Opcode.IGET_CHAR_QUICK),
new FieldOpcode('I', Opcode.IGET, Opcode.IGET_QUICK),
new FieldOpcode('F', Opcode.IGET, Opcode.IGET_QUICK),
new FieldOpcode('J', Opcode.IGET_WIDE, Opcode.IGET_WIDE_QUICK),
new FieldOpcode('D', Opcode.IGET_WIDE, Opcode.IGET_WIDE_QUICK),
new FieldOpcode('L', Opcode.IGET_OBJECT, Opcode.IGET_OBJECT_QUICK),
new FieldOpcode('[', Opcode.IGET_OBJECT, Opcode.IGET_OBJECT_QUICK),
new FieldOpcode('Z', Opcode.IPUT_BOOLEAN, Opcode.IPUT_BOOLEAN_QUICK),
new FieldOpcode('B', Opcode.IPUT_BYTE, Opcode.IPUT_BYTE_QUICK),
new FieldOpcode('S', Opcode.IPUT_SHORT, Opcode.IPUT_SHORT_QUICK),
new FieldOpcode('C', Opcode.IPUT_CHAR, Opcode.IPUT_CHAR_QUICK),
new FieldOpcode('I', Opcode.IPUT, Opcode.IPUT_QUICK),
new FieldOpcode('F', Opcode.IPUT, Opcode.IPUT_QUICK),
new FieldOpcode('J', Opcode.IPUT_WIDE, Opcode.IPUT_WIDE_QUICK),
new FieldOpcode('D', Opcode.IPUT_WIDE, Opcode.IPUT_WIDE_QUICK),
new FieldOpcode('L', Opcode.IPUT_OBJECT, Opcode.IPUT_OBJECT_QUICK),
new FieldOpcode('[', Opcode.IPUT_OBJECT, Opcode.IPUT_OBJECT_QUICK)
};
private final FieldOpcode[][] opcodeMap = new FieldOpcode[2][10];
private final Map<Opcode, Integer> opcodeValueTypeMap = new HashMap<Opcode, Integer>(30);
private static int getValueType(char type) {
switch (type) {
case 'Z':
case 'B':
case 'S':
case 'C':
case 'I':
case 'F':
return PRIMITIVE;
case 'J':
case 'D':
return WIDE;
case 'L':
case '[':
return REFERENCE;
}
throw new RuntimeException(String.format("Unknown type %s: ", type));
}
private static int getTypeIndex(char type) {
switch (type) {
case 'Z':
@ -194,47 +150,61 @@ public class OdexedFieldInstructionMapper {
case 'C':
return 3;
case 'I':
case 'F':
return 4;
case 'J':
case 'D':
case 'F':
return 5;
case 'L':
case '[':
case 'J':
return 6;
default:
case 'D':
return 7;
case 'L':
return 8;
case '[':
return 9;
}
throw new RuntimeException(String.format("Unknown type %s: ", type));
}
private static int getOpcodeSubtype(@Nonnull Opcode opcode) {
if (opcode.isOdexedInstanceQuick()) {
return 0;
} else if (opcode.isOdexedInstanceVolatile()) {
return 1;
} else if (opcode.isOdexedStaticVolatile()) {
return 2;
private static boolean isGet(@Nonnull Opcode opcode) {
return (opcode.flags & Opcode.SETS_REGISTER) != 0;
}
public OdexedFieldInstructionMapper(boolean isArt) {
FieldOpcode[] opcodes;
if (isArt) {
opcodes = artFieldOpcodes;
} else {
opcodes = dalvikFieldOpcodes;
}
for (FieldOpcode fieldOpcode: opcodes) {
opcodeMap[isGet(fieldOpcode.normalOpcode)?GET:PUT][getTypeIndex(fieldOpcode.type)] = fieldOpcode;
opcodeValueTypeMap.put(fieldOpcode.quickOpcode, getValueType(fieldOpcode.type));
if (fieldOpcode.volatileOpcode != null) {
opcodeValueTypeMap.put(fieldOpcode.volatileOpcode, getValueType(fieldOpcode.type));
}
}
throw new RuntimeException(String.format("Not an odexed field access opcode: %s", opcode.name));
}
@Nonnull
static Opcode getAndCheckDeodexedOpcodeForOdexedOpcode(@Nonnull String fieldType, @Nonnull Opcode odexedOpcode) {
int opcodeType = odexedOpcode.setsRegister()?0:1;
int opcodeSubType = getOpcodeSubtype(odexedOpcode);
int typeIndex = getTypeIndex(fieldType.charAt(0));
public Opcode getAndCheckDeodexedOpcode(@Nonnull String fieldType, @Nonnull Opcode odexedOpcode) {
FieldOpcode fieldOpcode = opcodeMap[isGet(odexedOpcode)?GET:PUT][getTypeIndex(fieldType.charAt(0))];
Opcode correctOdexedOpcode, deodexedOpcode;
correctOdexedOpcode = opcodeMap[opcodeType][opcodeSubType][0][typeIndex];
deodexedOpcode = opcodeMap[opcodeType][opcodeSubType][1][typeIndex];
if (correctOdexedOpcode != odexedOpcode) {
if (!isCompatible(odexedOpcode, fieldOpcode.type)) {
throw new AnalysisException(String.format("Incorrect field type \"%s\" for %s", fieldType,
odexedOpcode.name));
}
return deodexedOpcode;
return fieldOpcode.normalOpcode;
}
private boolean isCompatible(Opcode opcode, char type) {
Integer valueType = opcodeValueTypeMap.get(opcode);
if (valueType == null) {
throw new RuntimeException("Unexpected opcode: " + opcode.name);
}
return valueType == getValueType(type);
}
}