mirror of
https://github.com/revanced/smali.git
synced 2025-05-29 12:20:11 +02:00
Add support for calculating field offsets for art
This commit is contained in:
parent
64785d79a3
commit
c8c70ac58e
@ -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.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();
|
||||
}
|
||||
}
|
||||
|
@ -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,16 +530,171 @@ 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);
|
||||
|
||||
if (classPath.isArt) {
|
||||
return fieldOffset + getTypeSize(lastField.getType().charAt(0));
|
||||
} else {
|
||||
switch (lastField.getType().charAt(0)) {
|
||||
case 'J':
|
||||
case 'D':
|
||||
@ -561,6 +703,27 @@ public class ClassProto implements TypeProto {
|
||||
return fieldOffset + 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int getTypeSize(char type) {
|
||||
switch (type) {
|
||||
case 'J':
|
||||
case 'D':
|
||||
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() {
|
||||
return vtableSupplier.get();
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user