aboutsummaryrefslogtreecommitdiff
path: root/dexlib2/src/main/java/org/jf/dexlib2/analysis/MethodAnalyzer.java
diff options
context:
space:
mode:
Diffstat (limited to 'dexlib2/src/main/java/org/jf/dexlib2/analysis/MethodAnalyzer.java')
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/analysis/MethodAnalyzer.java341
1 files changed, 202 insertions, 139 deletions
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/MethodAnalyzer.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/MethodAnalyzer.java
index b7a15a01..7a51c96c 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/MethodAnalyzer.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/MethodAnalyzer.java
@@ -36,6 +36,7 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import org.jf.dexlib2.AccessFlags;
import org.jf.dexlib2.Opcode;
+import org.jf.dexlib2.base.reference.BaseMethodReference;
import org.jf.dexlib2.iface.*;
import org.jf.dexlib2.iface.instruction.*;
import org.jf.dexlib2.iface.instruction.formats.*;
@@ -89,10 +90,10 @@ public class MethodAnalyzer {
@Nullable private AnalysisException analysisException = null;
- //This is a dummy instruction that occurs immediately before the first real instruction. We can initialize the
- //register types for this instruction to the parameter types, in order to have them propagate to all of its
- //successors, e.g. the first real instruction, the first instructions in any exception handlers covering the first
- //instruction, etc.
+ // This is a dummy instruction that occurs immediately before the first real instruction. We can initialize the
+ // register types for this instruction to the parameter types, in order to have them propagate to all of its
+ // successors, e.g. the first real instruction, the first instructions in any exception handlers covering the first
+ // instruction, etc.
private final AnalyzedInstruction startOfMethod;
public MethodAnalyzer(@Nonnull ClassPath classPath, @Nonnull Method method,
@@ -110,27 +111,16 @@ public class MethodAnalyzer {
this.methodImpl = methodImpl;
- //override AnalyzedInstruction and provide custom implementations of some of the methods, so that we don't
- //have to handle the case this special case of instruction being null, in the main class
- startOfMethod = new AnalyzedInstruction(this, null, -1, methodImpl.getRegisterCount()) {
- public boolean setsRegister() {
- return false;
- }
-
- @Override
- public boolean setsWideRegister() {
- return false;
- }
-
- @Override
- public boolean setsRegister(int registerNumber) {
- return false;
+ // Override AnalyzedInstruction and provide custom implementations of some of the methods, so that we don't
+ // have to handle the case this special case of instruction being null, in the main class
+ startOfMethod = new AnalyzedInstruction(this, new ImmutableInstruction10x(Opcode.NOP), -1, methodImpl.getRegisterCount()) {
+ @Override protected boolean addPredecessor(AnalyzedInstruction predecessor) {
+ throw new UnsupportedOperationException();
}
- @Override
- public int getDestinationRegister() {
- assert false;
- return -1;
+ @Override @Nonnull
+ public RegisterType getPredecessorRegisterType(@Nonnull AnalyzedInstruction predecessor, int registerNumber) {
+ throw new UnsupportedOperationException();
}
};
@@ -141,6 +131,7 @@ public class MethodAnalyzer {
analyze();
}
+ @Nonnull
public ClassPath getClassPath() {
return classPath;
}
@@ -362,6 +353,7 @@ public class MethodAnalyzer {
private void overridePredecessorRegisterTypeAndPropagateChanges(
@Nonnull AnalyzedInstruction analyzedInstruction, @Nonnull AnalyzedInstruction predecessor,
int registerNumber, @Nonnull RegisterType registerType) {
+
BitSet changedInstructions = new BitSet(analyzedInstructions.size());
if (!analyzedInstruction.overridePredecessorRegisterType(
@@ -383,6 +375,28 @@ public class MethodAnalyzer {
}
}
+ private void initializeRefAndPropagateChanges(@Nonnull AnalyzedInstruction analyzedInstruction,
+ int registerNumber, @Nonnull RegisterType registerType) {
+
+ BitSet changedInstructions = new BitSet(analyzedInstructions.size());
+
+ if (!analyzedInstruction.setPostRegisterType(registerNumber, registerType)) {
+ return;
+ }
+
+ propagateRegisterToSuccessors(analyzedInstruction, registerNumber, changedInstructions, false);
+
+ propagateChanges(changedInstructions, registerNumber, false);
+
+ if (registerType.category == RegisterType.LONG_LO) {
+ checkWidePair(registerNumber, analyzedInstruction);
+ setPostRegisterTypeAndPropagateChanges(analyzedInstruction, registerNumber+1, RegisterType.LONG_HI_TYPE);
+ } else if (registerType.category == RegisterType.DOUBLE_LO) {
+ checkWidePair(registerNumber, analyzedInstruction);
+ setPostRegisterTypeAndPropagateChanges(analyzedInstruction, registerNumber+1, RegisterType.DOUBLE_HI_TYPE);
+ }
+ }
+
private void setPostRegisterTypeAndPropagateChanges(@Nonnull AnalyzedInstruction analyzedInstruction,
int registerNumber, @Nonnull RegisterType registerType) {
@@ -1176,32 +1190,46 @@ public class MethodAnalyzer {
setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, castRegisterType);
}
- static boolean canNarrowAfterInstanceOf(AnalyzedInstruction analyzedInstanceOfInstruction,
- AnalyzedInstruction analyzedIfInstruction, ClassPath classPath) {
+ public static boolean isNotWideningConversion(RegisterType originalType, RegisterType newType) {
+ if (originalType.type == null || newType.type == null) {
+ return true;
+ }
+ if (originalType.type.isInterface()) {
+ return newType.type.implementsInterface(originalType.type.getType());
+ } else {
+ TypeProto commonSuperclass = newType.type.getCommonSuperclass(originalType.type);
+ if (commonSuperclass.getType().equals(originalType.type.getType())) {
+ return true;
+ }
+ if (commonSuperclass.getType().equals(newType.type.getType())) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ static boolean canPropagateTypeAfterInstanceOf(AnalyzedInstruction analyzedInstanceOfInstruction,
+ AnalyzedInstruction analyzedIfInstruction, ClassPath classPath) {
+ if (!classPath.isArt()) {
+ return false;
+ }
+
Instruction ifInstruction = analyzedIfInstruction.instruction;
- assert analyzedIfInstruction.instruction != null;
if (((Instruction21t)ifInstruction).getRegisterA() == analyzedInstanceOfInstruction.getDestinationRegister()) {
Reference reference = ((Instruction22c)analyzedInstanceOfInstruction.getInstruction()).getReference();
RegisterType registerType = RegisterType.getRegisterType(classPath, (TypeReference)reference);
- if (registerType.type != null && !registerType.type.isInterface()) {
- int objectRegister = ((TwoRegisterInstruction)analyzedInstanceOfInstruction.getInstruction())
- .getRegisterB();
+ try {
+ if (registerType.type != null && !registerType.type.isInterface()) {
+ int objectRegister = ((TwoRegisterInstruction)analyzedInstanceOfInstruction.getInstruction())
+ .getRegisterB();
- RegisterType originalType = analyzedIfInstruction.getPreInstructionRegisterType(objectRegister);
+ RegisterType originalType = analyzedIfInstruction.getPreInstructionRegisterType(objectRegister);
- if (originalType.type != null) {
- // Only override if we're going from an interface to a class, or are going to a narrower class
- if (originalType.type.isInterface()) {
- return true;
- } else {
- TypeProto commonSuperclass = registerType.type.getCommonSuperclass(originalType.type);
- // only if it's a narrowing conversion
- if (commonSuperclass.getType().equals(originalType.type.getType())) {
- return true;
- }
- }
+ return isNotWideningConversion(originalType, registerType);
}
+ } catch (UnresolvedClassException ex) {
+ return false;
}
}
return false;
@@ -1210,21 +1238,16 @@ public class MethodAnalyzer {
/**
* Art uses a peephole optimization for an if-eqz or if-nez that occur immediately after an instance-of. It will
* narrow the type if possible, and then NOP out any corresponding check-cast instruction later on
- *
- * TODO: Is this still safe to do even for dalvik odexes? I think it should be..
*/
private void analyzeIfEqzNez(@Nonnull AnalyzedInstruction analyzedInstruction) {
- int instructionIndex = analyzedInstruction.getInstructionIndex();
- if (instructionIndex > 0) {
- AnalyzedInstruction prevAnalyzedInstruction = analyzedInstructions.valueAt(instructionIndex - 1);
- if (prevAnalyzedInstruction.instruction != null &&
- prevAnalyzedInstruction.instruction.getOpcode() == Opcode.INSTANCE_OF) {
- if (canNarrowAfterInstanceOf(prevAnalyzedInstruction, analyzedInstruction, classPath)) {
- // Propagate the original type to the failing branch, and the new type to the successful branch
- int narrowingRegister = ((Instruction22c)prevAnalyzedInstruction.instruction).getRegisterB();
- RegisterType originalType = analyzedInstruction.getPreInstructionRegisterType(narrowingRegister);
- RegisterType newType = RegisterType.getRegisterType(classPath,
- (TypeReference)((Instruction22c)prevAnalyzedInstruction.instruction).getReference());
+ if (classPath.isArt()) {
+ int instructionIndex = analyzedInstruction.getInstructionIndex();
+ if (instructionIndex > 0) {
+ if (analyzedInstruction.getPredecessorCount() != 1) {
+ return;
+ }
+ AnalyzedInstruction prevAnalyzedInstruction = analyzedInstruction.getPredecessors().first();
+ if (prevAnalyzedInstruction.instruction.getOpcode() == Opcode.INSTANCE_OF) {
AnalyzedInstruction fallthroughInstruction = analyzedInstructions.valueAt(
analyzedInstruction.getInstructionIndex() + 1);
@@ -1233,16 +1256,25 @@ public class MethodAnalyzer {
((Instruction21t)analyzedInstruction.instruction).getCodeOffset();
AnalyzedInstruction branchInstruction = analyzedInstructions.get(nextAddress);
- if (analyzedInstruction.instruction.getOpcode() == Opcode.IF_EQZ) {
- overridePredecessorRegisterTypeAndPropagateChanges(fallthroughInstruction, analyzedInstruction,
- narrowingRegister, newType);
- overridePredecessorRegisterTypeAndPropagateChanges(branchInstruction, analyzedInstruction,
- narrowingRegister, originalType);
- } else {
- overridePredecessorRegisterTypeAndPropagateChanges(fallthroughInstruction, analyzedInstruction,
- narrowingRegister, originalType);
- overridePredecessorRegisterTypeAndPropagateChanges(branchInstruction, analyzedInstruction,
- narrowingRegister, newType);
+ int narrowingRegister = ((Instruction22c)prevAnalyzedInstruction.instruction).getRegisterB();
+ RegisterType originalType = analyzedInstruction.getPreInstructionRegisterType(narrowingRegister);
+
+ Instruction22c instanceOfInstruction = (Instruction22c)prevAnalyzedInstruction.instruction;
+ RegisterType newType = RegisterType.getRegisterType(classPath,
+ (TypeReference)instanceOfInstruction.getReference());
+
+ for (int register : analyzedInstruction.getSetRegisters()) {
+ if (analyzedInstruction.instruction.getOpcode() == Opcode.IF_EQZ) {
+ overridePredecessorRegisterTypeAndPropagateChanges(fallthroughInstruction,
+ analyzedInstruction, register, newType);
+ overridePredecessorRegisterTypeAndPropagateChanges(branchInstruction, analyzedInstruction,
+ register, originalType);
+ } else {
+ overridePredecessorRegisterTypeAndPropagateChanges(fallthroughInstruction,
+ analyzedInstruction, register, originalType);
+ overridePredecessorRegisterTypeAndPropagateChanges(branchInstruction, analyzedInstruction,
+ register, newType);
+ }
}
}
}
@@ -1380,44 +1412,32 @@ public class MethodAnalyzer {
}
private void analyzeInvokeDirectCommon(@Nonnull AnalyzedInstruction analyzedInstruction, int objectRegister) {
- //the only time that an invoke instruction changes a register type is when using invoke-direct on a
- //constructor (<init>) method, which changes the uninitialized reference (and any register that the same
- //uninit reference has been copied to) to an initialized reference
-
- ReferenceInstruction instruction = (ReferenceInstruction)analyzedInstruction.instruction;
-
- MethodReference methodReference = (MethodReference)instruction.getReference();
-
- if (!methodReference.getName().equals("<init>")) {
- return;
- }
-
- RegisterType objectRegisterType = analyzedInstruction.getPreInstructionRegisterType(objectRegister);
-
- if (objectRegisterType.category != RegisterType.UNINIT_REF &&
- objectRegisterType.category != RegisterType.UNINIT_THIS) {
- return;
- }
+ // This handles the case of invoking a constructor on an uninitialized reference. This propagates the
+ // initialized type for the object register, and also any known aliased registers.
+ //
+ // In some cases, unrelated uninitialized references may not have been propagated past this instruction. This
+ // happens when propagating those types and the type of object register of this instruction isn't known yet.
+ // In this case, we can't determine if the uninitialized reference being propagated in an alias of the object
+ // register, so we don't stop propagation.
+ //
+ // We check for any of these unpropagated uninitialized references here and propagate them.
+ if (analyzedInstruction.isInvokeInit()) {
+ RegisterType uninitRef = analyzedInstruction.getPreInstructionRegisterType(objectRegister);
+ if (uninitRef.category != RegisterType.UNINIT_REF && uninitRef.category != RegisterType.UNINIT_THIS) {
+ assert analyzedInstruction.getSetRegisters().isEmpty();
+ return;
+ }
- setPostRegisterTypeAndPropagateChanges(analyzedInstruction, objectRegister,
- RegisterType.getRegisterType(RegisterType.REFERENCE, objectRegisterType.type));
+ RegisterType initRef = RegisterType.getRegisterType(RegisterType.REFERENCE, uninitRef.type);
- for (int i=0; i<analyzedInstruction.postRegisterMap.length; i++) {
- RegisterType postInstructionRegisterType = analyzedInstruction.postRegisterMap[i];
- if (postInstructionRegisterType.category == RegisterType.UNKNOWN) {
- RegisterType preInstructionRegisterType =
- analyzedInstruction.getPreInstructionRegisterType(i);
+ for (int register: analyzedInstruction.getSetRegisters()) {
+ RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(register);
- if (preInstructionRegisterType.category == RegisterType.UNINIT_REF ||
- preInstructionRegisterType.category == RegisterType.UNINIT_THIS) {
- RegisterType registerType;
- if (preInstructionRegisterType.equals(objectRegisterType)) {
- registerType = analyzedInstruction.postRegisterMap[objectRegister];
- } else {
- registerType = preInstructionRegisterType;
- }
-
- setPostRegisterTypeAndPropagateChanges(analyzedInstruction, i, registerType);
+ if (registerType == uninitRef) {
+ setPostRegisterTypeAndPropagateChanges(analyzedInstruction, register, initRef);
+ } else {
+ // This is unrelated uninitialized reference. propagate it as-is
+ setPostRegisterTypeAndPropagateChanges(analyzedInstruction, register, registerType);
}
}
}
@@ -1695,13 +1715,13 @@ public class MethodAnalyzer {
// fieldClass is now the first accessible class found. Now. we need to make sure that the field is
// actually valid for this class
- resolvedField = classPath.getClass(fieldClass.getType()).getFieldByOffset(fieldOffset);
- if (resolvedField == null) {
+ FieldReference newResolvedField = classPath.getClass(fieldClass.getType()).getFieldByOffset(fieldOffset);
+ if (newResolvedField == null) {
throw new ExceptionWithContext("Couldn't find accessible class while resolving field %s",
ReferenceUtil.getShortFieldDescriptor(resolvedField));
}
- resolvedField = new ImmutableFieldReference(fieldClass.getType(), resolvedField.getName(),
- resolvedField.getType());
+ resolvedField = new ImmutableFieldReference(fieldClass.getType(), newResolvedField.getName(),
+ newResolvedField.getType());
}
String fieldType = resolvedField.getType();
@@ -1733,41 +1753,9 @@ public class MethodAnalyzer {
targetMethod = (MethodReference)instruction.getReference();
}
- TypeProto typeProto = classPath.getClass(targetMethod.getDefiningClass());
- int methodIndex;
- try {
- methodIndex = typeProto.findMethodIndexInVtable(targetMethod);
- } catch (UnresolvedClassException ex) {
- return true;
- }
-
- if (methodIndex < 0) {
- return true;
- }
-
- Method replacementMethod = typeProto.getMethodByVtableIndex(methodIndex);
- assert replacementMethod != null;
- while (true) {
- String superType = typeProto.getSuperclass();
- if (superType == null) {
- break;
- }
- typeProto = classPath.getClass(superType);
- Method resolvedMethod = typeProto.getMethodByVtableIndex(methodIndex);
- if (resolvedMethod == null) {
- break;
- }
-
- if (!resolvedMethod.equals(replacementMethod)) {
- if (!AnalyzedMethodUtil.canAccess(typeProto, replacementMethod, true, true, true)) {
- continue;
- }
-
- replacementMethod = resolvedMethod;
- }
- }
+ MethodReference replacementMethod = normalizeMethodReference(targetMethod);
- if (replacementMethod.equals(method)) {
+ if (replacementMethod == null || replacementMethod.equals(targetMethod)) {
return true;
}
@@ -1839,7 +1827,9 @@ public class MethodAnalyzer {
// no need to check class access for invoke-super. A class can obviously access its superclass.
ClassDef thisClass = classPath.getClassDef(method.getDefiningClass());
- if (!isSuper && !TypeUtils.canAccessClass(
+ if (classPath.getClass(resolvedMethod.getDefiningClass()).isInterface()) {
+ resolvedMethod = new ReparentedMethodReference(resolvedMethod, objectRegisterTypeProto.getType());
+ } else if (!isSuper && !TypeUtils.canAccessClass(
thisClass.getType(), classPath.getClassDef(resolvedMethod.getDefiningClass()))) {
// the class is not accessible. So we start looking at objectRegisterTypeProto (which may be different
@@ -1860,13 +1850,20 @@ public class MethodAnalyzer {
MethodReference newResolvedMethod =
classPath.getClass(methodClass.getType()).getMethodByVtableIndex(methodIndex);
if (newResolvedMethod == null) {
- // TODO: fix NPE here
throw new ExceptionWithContext("Couldn't find accessible class while resolving method %s",
ReferenceUtil.getMethodDescriptor(resolvedMethod, true));
}
resolvedMethod = newResolvedMethod;
resolvedMethod = new ImmutableMethodReference(methodClass.getType(), resolvedMethod.getName(),
resolvedMethod.getParameterTypes(), resolvedMethod.getReturnType());
+
+ }
+
+ if (normalizeVirtualMethods) {
+ MethodReference replacementMethod = normalizeMethodReference(resolvedMethod);
+ if (replacementMethod != null) {
+ resolvedMethod = replacementMethod;
+ }
}
Instruction deodexedInstruction;
@@ -1967,4 +1964,70 @@ public class MethodAnalyzer {
"pair because it is the last register.", registerNumber));
}
}
+
+ @Nullable
+ private MethodReference normalizeMethodReference(@Nonnull MethodReference methodRef) {
+ TypeProto typeProto = classPath.getClass(methodRef.getDefiningClass());
+ int methodIndex;
+ try {
+ methodIndex = typeProto.findMethodIndexInVtable(methodRef);
+ } catch (UnresolvedClassException ex) {
+ return null;
+ }
+
+ if (methodIndex < 0) {
+ return null;
+ }
+
+ ClassProto thisClass = (ClassProto)classPath.getClass(method.getDefiningClass());
+
+ Method replacementMethod = typeProto.getMethodByVtableIndex(methodIndex);
+ assert replacementMethod != null;
+ while (true) {
+ String superType = typeProto.getSuperclass();
+ if (superType == null) {
+ break;
+ }
+ typeProto = classPath.getClass(superType);
+ Method resolvedMethod = typeProto.getMethodByVtableIndex(methodIndex);
+ if (resolvedMethod == null) {
+ break;
+ }
+
+ if (!resolvedMethod.equals(replacementMethod)) {
+ if (!AnalyzedMethodUtil.canAccess(thisClass, resolvedMethod, false, false, true)) {
+ continue;
+ }
+
+ replacementMethod = resolvedMethod;
+ }
+ }
+ return replacementMethod;
+ }
+
+ private static class ReparentedMethodReference extends BaseMethodReference {
+ private final MethodReference baseReference;
+ private final String definingClass;
+
+ public ReparentedMethodReference(MethodReference baseReference, String definingClass) {
+ this.baseReference = baseReference;
+ this.definingClass = definingClass;
+ }
+
+ @Override @Nonnull public String getName() {
+ return baseReference.getName();
+ }
+
+ @Override @Nonnull public List<? extends CharSequence> getParameterTypes() {
+ return baseReference.getParameterTypes();
+ }
+
+ @Override @Nonnull public String getReturnType() {
+ return baseReference.getReturnType();
+ }
+
+ @Nonnull @Override public String getDefiningClass() {
+ return definingClass;
+ }
+ }
} \ No newline at end of file