diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/AnalyzedInstruction.java b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/AnalyzedInstruction.java new file mode 100644 index 00000000..69617898 --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/AnalyzedInstruction.java @@ -0,0 +1,65 @@ +package org.jf.dexlib.Code.Analysis; + +import org.jf.dexlib.Code.Instruction; + +import java.util.LinkedList; + +public class AnalyzedInstruction { + /** + * The actual instruction + */ + protected final Instruction instruction; + + /** + * The address of the instruction, in 2-byte code blocks + */ + protected final int codeAddress; + + /** + * Instructions that can pass on execution to this one during normal execution + */ + protected LinkedList predecessors = new LinkedList(); + + /** + * Instructions that can execution could pass on to next during normal execution + */ + protected LinkedList successors = new LinkedList(); + + /** + * This is set to true when this instruction follows an odexed instruction that couldn't be deodexed. In this case + * the unodexable instruction is guaranteed to throw an NPE, so anything following it is dead, up until a non-dead + * code path merges in. And more importantly, the code following the unodexable instruction isn't verifiable in + * some cases, if it depends on the return/field type of the unodexeable instruction. Meaning that if the "dead" + * code was left in, dalvik would reject it because it couldn't verify the register types. In some cases, this + * dead code could be left in without ill-effect, but it's easier to always remove it, which is always valid. Since + * it is dead code, removing it won't have any effect. + */ + protected boolean dead = false; + + public AnalyzedInstruction(Instruction instruction, int codeAddress) { + this.instruction = instruction; + this.codeAddress = codeAddress; + } + + public int getCodeAddress() { + return codeAddress; + } + + protected void addPredecessor(AnalyzedInstruction predecessor) { + predecessors.add(predecessor); + } + + /** + * @return true if the successor was added or false if it wasn't added because it already existed + */ + protected boolean addSuccessor(AnalyzedInstruction successor) { + for (AnalyzedInstruction instruction: successors) { + if (instruction == successor) { + return false; + } + } + successors.add(successor); + return true; + } +} + diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/MethodAnalyzer.java b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/MethodAnalyzer.java new file mode 100644 index 00000000..4624adb4 --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/MethodAnalyzer.java @@ -0,0 +1,212 @@ +package org.jf.dexlib.Code.Analysis; + +import org.jf.dexlib.ClassDataItem; +import org.jf.dexlib.Code.Instruction; +import org.jf.dexlib.Code.MultiOffsetInstruction; +import org.jf.dexlib.Code.OffsetInstruction; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.CodeItem; + +import java.util.Arrays; +import java.util.HashSet; + +public class MethodAnalyzer { + private final HashSet registerInfoCache = new HashSet(); + private final ClassDataItem.EncodedMethod encodedMethod; + + private AnalyzedInstruction[] instructions; + + //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 AnalyzedInstruction startOfMethod; + + public MethodAnalyzer(ClassDataItem.EncodedMethod encodedMethod) { + if (encodedMethod == null) { + throw new IllegalArgumentException("encodedMethod cannot be null"); + } + if (encodedMethod.codeItem == null) { + throw new IllegalArgumentException("The method has no code"); + } + this.encodedMethod = encodedMethod; + buildInstructionList(); + } + + public AnalyzedInstruction[] analyze() { + return null; + } + + private void buildInstructionList() { + assert encodedMethod != null; + assert encodedMethod.codeItem != null; + + startOfMethod = new AnalyzedInstruction(null, -1); + + Instruction[] insns = encodedMethod.codeItem.getInstructions(); + + instructions = new AnalyzedInstruction[insns.length]; + + //first, create all the instructions and populate the instructionAddresses array + int currentCodeAddress = 0; + for (int i=0; i currentCodeAddress); + + currentTry = tryItem; + + currentExceptionHandlers = buildExceptionHandlerArray(tryItem); + } + } + + //if we're inside a try block, and the instruction can throw an exception, then add the exception handlers + //for the current instruction + if (currentTry != null && instructionOpcode.canThrow()) { + exceptionHandlers[i] = currentExceptionHandlers; + } + } + + //finally, populate the successors and predecessors for each instruction + for (int i=0; i (start + 1)) { + int index = (start + end) / 2; + + if (instructions[index].codeAddress < address) { + start = index; + } else { + end = index; + } + } + + if (end < instructions.length && instructions[end].codeAddress == address) { + return end; + } + + throw new RuntimeException("No instruction at address " + address); + } + + private AnalyzedInstruction getInstructionByAddress(int address) { + int index = getInstructionIndexByAddress(address); + + assert index < instructions.length; + assert instructions[index] != null; + return instructions[index]; + } +} diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/RegisterInfo.java b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/RegisterInfo.java new file mode 100644 index 00000000..36c83adf --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/RegisterInfo.java @@ -0,0 +1,10 @@ +package org.jf.dexlib.Code.Analysis; + +import java.util.LinkedList; + +public class RegisterInfo { + private final LinkedList possibleRegisterTypes = new LinkedList(); + + protected RegisterInfo() { + } +} diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/RegisterType.java b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/RegisterType.java new file mode 100644 index 00000000..6af7a5ee --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/RegisterType.java @@ -0,0 +1,56 @@ +package org.jf.dexlib.Code.Analysis; + +import org.jf.dexlib.TypeIdItem; + +public class RegisterType { + public final Category category; + public final TypeIdItem type; + + protected RegisterType(Category category, TypeIdItem type) { + this.category = category; + this.type = type; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + RegisterType that = (RegisterType) o; + + if (category != that.category) return false; + if (type != null ? !type.equals(that.type) : that.type != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = category.hashCode(); + result = 31 * result + (type != null ? type.hashCode() : 0); + return result; + } + + + /*private static RegisterType[][] mergeTable = + { + //Unknown Null Nonreference Reference Conflicted + {Unknown, Null, NonReference, Reference, Conflicted}, //Unknown + {Null, Null, NonReference, Reference, Conflicted}, //Null + {NonReference, NonReference, NonReference, Conflicted, Conflicted}, //NonReference + {Reference, Reference, Conflicted, Reference, Conflicted}, //Referenced + {Conflicted, Conflicted, Conflicted, Conflicted, Conflicted}, //Conflicted + };*/ + + /*public static RegisterType mergeRegisterTypes(RegisterType type1, RegisterType type2) { + return mergeTable[type1.ordinal()][type2.ordinal()]; + }*/ + + public static enum Category { + Unknown, + Null, + NonReference, + Reference, + Conflicted + } +} diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/ValidationException.java b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/ValidationException.java new file mode 100644 index 00000000..e427c242 --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/ValidationException.java @@ -0,0 +1,7 @@ +package org.jf.dexlib.Code.Analysis; + +public class ValidationException extends RuntimeException { + public ValidationException(String errorMessage) { + super(errorMessage); + } +}