diff --git a/baksmali/src/main/java/org/jf/baksmali/Adaptors/CatchMethodItem.java b/baksmali/src/main/java/org/jf/baksmali/Adaptors/CatchMethodItem.java index 6ba9cb09..e99566f9 100644 --- a/baksmali/src/main/java/org/jf/baksmali/Adaptors/CatchMethodItem.java +++ b/baksmali/src/main/java/org/jf/baksmali/Adaptors/CatchMethodItem.java @@ -46,18 +46,16 @@ public class CatchMethodItem extends MethodItem { this.exceptionType = exceptionType; tryStartLabel = labelCache.internLabel(new LabelMethodItem(startAddress, "try_start_")); - tryStartLabel.setUncommented(); + //use the address from the last covered instruction, but make the label //name refer to the address of the next instruction tryEndLabel = labelCache.internLabel(new EndTryLabelMethodItem(codeAddress, endAddress)); - tryEndLabel.setUncommented(); if (exceptionType == null) { handlerLabel = labelCache.internLabel(new LabelMethodItem(handlerAddress, "catchall_")); } else { handlerLabel = labelCache.internLabel(new LabelMethodItem(handlerAddress, "catch_")); } - handlerLabel.setUncommented(); } public LabelMethodItem getTryStartLabel() { diff --git a/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/ArrayDataMethodItem.java b/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/ArrayDataMethodItem.java index 9e7659eb..c8a6c53f 100644 --- a/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/ArrayDataMethodItem.java +++ b/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/ArrayDataMethodItem.java @@ -37,55 +37,30 @@ import java.io.IOException; import java.util.Iterator; public class ArrayDataMethodItem extends InstructionMethodItem { - private final boolean dead; - - public ArrayDataMethodItem(CodeItem codeItem, int codeAddress, boolean dead, - ArrayDataPseudoInstruction instruction) { + public ArrayDataMethodItem(CodeItem codeItem, int codeAddress, ArrayDataPseudoInstruction instruction) { super(codeItem, codeAddress, instruction); - this.dead = dead; } public boolean writeTo(IndentingWriter writer) throws IOException { - if (dead) { - writer.write("#.array-data 0x"); - writer.printLongAsHex(instruction.getElementWidth()); - writer.write('\n'); + writer.write(".array-data 0x"); + writer.printLongAsHex(instruction.getElementWidth()); + writer.write('\n'); - Iterator iterator = instruction.getElements(); - while (iterator.hasNext()) { - ArrayDataPseudoInstruction.ArrayElement element = iterator.next(); + writer.indent(4); + Iterator iterator = instruction.getElements(); + while (iterator.hasNext()) { + ArrayDataPseudoInstruction.ArrayElement element = iterator.next(); - writer.write("# "); - for (int i=0; i iterator = instruction.getElements(); - while (iterator.hasNext()) { - ArrayDataPseudoInstruction.ArrayElement element = iterator.next(); - - for (int i=0; i implements Iterable { private final List labels; - private final boolean dead; - public PackedSwitchMethodItem(MethodDefinition methodDefinition, CodeItem codeItem, int codeAddress, boolean dead, + public PackedSwitchMethodItem(MethodDefinition methodDefinition, CodeItem codeItem, int codeAddress, PackedSwitchDataPseudoInstruction instruction) { super(codeItem, codeAddress, instruction); @@ -58,36 +57,21 @@ public class PackedSwitchMethodItem extends InstructionMethodItem implements Iterable { private final List targets; - private final boolean dead; - public SparseSwitchMethodItem(MethodDefinition methodDefinition, CodeItem codeItem, int codeAddress, boolean dead, + public SparseSwitchMethodItem(MethodDefinition methodDefinition, CodeItem codeItem, int codeAddress, SparseSwitchDataPseudoInstruction instruction) { super(codeItem, codeAddress, instruction); @@ -61,37 +60,23 @@ public class SparseSwitchMethodItem extends InstructionMethodItem "); - target.Target.writeTo(writer); - writer.write('\n'); - } - writer.write("#.end sparse-switch"); - } else { - writer.write(".sparse-switch\n"); - writer.indent(4); - for (SparseSwitchTarget target: targets) { - IntegerRenderer.writeTo(writer, target.Key); - writer.write(" -> "); - target.Target.writeTo(writer); - writer.write('\n'); - } - writer.deindent(4); - writer.write(".end sparse-switch"); + writer.write(".sparse-switch\n"); + writer.indent(4); + for (SparseSwitchTarget target: targets) { + IntegerRenderer.writeTo(writer, target.Key); + writer.write(" -> "); + target.Target.writeTo(writer); + writer.write('\n'); } + writer.deindent(4); + writer.write(".end sparse-switch"); return true; } diff --git a/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/UnresolvedNullReferenceMethodItem.java b/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/UnresolvedOdexInstructionMethodItem.java similarity index 56% rename from baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/UnresolvedNullReferenceMethodItem.java rename to baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/UnresolvedOdexInstructionMethodItem.java index 426a1f0f..0f4b901c 100644 --- a/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/UnresolvedNullReferenceMethodItem.java +++ b/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/UnresolvedOdexInstructionMethodItem.java @@ -29,49 +29,23 @@ package org.jf.baksmali.Adaptors.Format; import org.jf.baksmali.IndentingWriter; -import org.jf.dexlib.Code.Format.UnresolvedNullReference; +import org.jf.dexlib.Code.Format.UnresolvedOdexInstruction; import org.jf.dexlib.CodeItem; import java.io.IOException; -public class UnresolvedNullReferenceMethodItem extends InstructionMethodItem { - public final boolean isLastInstruction; - - public UnresolvedNullReferenceMethodItem(CodeItem codeItem, int codeAddress, UnresolvedNullReference instruction, - boolean isLastInstruction) { +public class UnresolvedOdexInstructionMethodItem extends InstructionMethodItem { + public UnresolvedOdexInstructionMethodItem(CodeItem codeItem, int codeAddress, UnresolvedOdexInstruction instruction) { super(codeItem, codeAddress, instruction); - this.isLastInstruction = isLastInstruction; } public boolean writeTo(IndentingWriter writer) throws IOException { - switch (instruction.OriginalInstruction.opcode) - { - case INVOKE_VIRTUAL_QUICK_RANGE: - case INVOKE_SUPER_QUICK_RANGE: - writeInvokeRangeTo(writer); - return true; - default: - writeThrowTo(writer); - return true; - } - } - - private void writeInvokeRangeTo(IndentingWriter writer) throws IOException { - writer.write("#Replaced unresolvable optimized invoke-*-range-quick instruction\n"); - writer.write("#with a generic method call that will throw a NullPointerException\n"); - writer.write("invoke-virtual/range {"); - writeRegister(writer, instruction.ObjectRegisterNum); - writer.write(" .. "); - writeRegister(writer, instruction.ObjectRegisterNum); - writer.write("}, Ljava/lang/Object;->hashCode()I"); - if (isLastInstruction) { - writer.write('\n'); - writer.write("goto/32 0"); - } + writeThrowTo(writer); + return true; } private void writeThrowTo(IndentingWriter writer) throws IOException { - writer.write("#Replaced unresolvable optimized instruction with a throw\n"); + writer.write("#Replaced unresolvable odex instruction with a throw\n"); writer.write("throw "); writeRegister(writer, instruction.ObjectRegisterNum); } diff --git a/baksmali/src/main/java/org/jf/baksmali/Adaptors/LabelMethodItem.java b/baksmali/src/main/java/org/jf/baksmali/Adaptors/LabelMethodItem.java index 378d14e2..5cab6826 100644 --- a/baksmali/src/main/java/org/jf/baksmali/Adaptors/LabelMethodItem.java +++ b/baksmali/src/main/java/org/jf/baksmali/Adaptors/LabelMethodItem.java @@ -36,7 +36,6 @@ import java.io.IOException; public class LabelMethodItem extends MethodItem { private final String labelPrefix; private int labelSequence; - private boolean isCommentedOut = true; public LabelMethodItem(int codeAddress, String labelPrefix) { super(codeAddress); @@ -47,14 +46,6 @@ public class LabelMethodItem extends MethodItem { return 0; } - public boolean isCommentedOut() { - return isCommentedOut; - } - - public void setUncommented() { - this.isCommentedOut = false; - } - public int compareTo(MethodItem methodItem) { int result = super.compareTo(methodItem); diff --git a/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java b/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java index ecdca0c2..6995a071 100644 --- a/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java +++ b/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java @@ -300,11 +300,7 @@ public class MethodDefinition { } for (LabelMethodItem labelMethodItem: labelCache.getLabels()) { - if (labelMethodItem.isCommentedOut()) { - methodItems.add(new CommentedOutMethodItem(labelMethodItem)); - } else { - methodItems.add(labelMethodItem); - } + methodItems.add(labelMethodItem); } Collections.sort(methodItems); @@ -372,51 +368,19 @@ public class MethodDefinition { List instructions = methodAnalyzer.getInstructions(); - AnalyzedInstruction lastInstruction = null; - - for (int i=instructions.size()-1; i>=0; i--) { - AnalyzedInstruction instruction = instructions.get(i); - - if (!instruction.isDead()) { - lastInstruction = instruction; - break; - } - } - - boolean lastIsUnreachable = false; - int currentCodeAddress = 0; for (int i=0; i()V - if-eqz v0, :here2 + if-eqz v0, :here #this is the unresolvable instruction. v0 is always null, - #and this will always throw an exception. Everything below - #here, until the here2: label is dead code, and should be - #commented out. This instruction itself should be be replaced - #with a call to Ljava/lang/Object;->hashCode()I + #and this will always throw an exception. It should be + #replaced with throw v0. invoke-virtual {v0}, Lrandomclass;->getSuperclass()Lsuperclass; move-result-object v1 - #a branch to outside the dead code. The branch label should not - #be commented out, because there is a non-dead instruction - #that branches to it - if-eqz v0, :here2 - - - #a branch to inside the dead code. the branch label should be - #commented out if-eqz v0, :here - #another branch to outside the dead code. In this case, the "dead" - #instruction is the first instruction that references the label. - #the label should not be commented out, because it is referenced - #be a non-dead instruction - if-eqz v0, :here3 - - #one more branch to out the dead code. the branch label should be - #commented out, because there are no other non-dead instructions - #referenceding it - if-eqz v0, :here4 + #this would normally be deodexable, except that it follows + #the above un-deodexeable instruction, which prevents the + #propagation of any register information. It can never be + #reached, and should be replaced with throw v2 + invoke-virtual {v2}, Lsuperclass;->somemethod()V #another odexed instruction that uses the result of the - #first unresolveable odex instruction. this should - #appear as a commented invoke-virtual-quick instruction + #first unresolveable odex instruction. This should + #be replaced with throw v1 invoke-virtual {v1}, Lsuperclass;->somemethod()V :here - #a resolveable odex instruction in the dead code. It should be resolved, - #but still commented out - invoke-virtual {v2}, Lsuperclass;->somemethod()V - - - const v0, 1 - new-array v0, v0, [I - - #this should be marked as dead, and so should the corresponding .sparse-switch structure - fill-array-data v0, :array-data - - const v0, 0 - - #this should be marked as dead, and so should the corresponding .sparse-switch structure - sparse-switch v0, :sparse-switch - - #this should be marked as dead, and so should the corresponding .packed-switch structure - packed-switch v0, :packed-switch - - - :here2 - #and we're back to the non-dead code invoke-virtual {v2}, Lsuperclass;->somemethod()V if-nez v0, :here3 - return-void - - #this should be marked as dead - :packed-switch - .packed-switch 0x0 - :here - .end packed-switch - - #this should be marked as dead - :sparse-switch - .sparse-switch - 0x0 -> :here - .end sparse-switch - - #this should be marked as dead - :array-data - .array-data 0x4 - 0x0t 0x0t 0x0t 0x0t - .end array-data -.end method - -.method public static UnresolvedInstructionTest1()Lsuperclass; - .registers 2 - const v0, 0 - - #this is an unresolvable instruction, due to v0 always being null - #this instruction should be replaced with "throw v0", followed by - #a "goto/32 0", since it would otherwise be the last instruction - #in the method, which isn't allowed - invoke-virtual/range {v0 .. v0}, Lrandomclass;->getSuperclass()Lsuperclass; - - #the following instructions should be commented out - move-result-object v1 - return-object v1 -.end method - -.method public static UnresolvedInstructionTest2()Lsuperclass; - .registers 2 - const v0, 0 - - if-eqz v0, :here - - #this is an unresolvable instruction, due to v0 always being null - #this instruction should be replaced with "throw v0". There shouldn't - #be a "goto/32 0" afterwards, since it won't be the last instruction - #in the method. - invoke-virtual/range {v0 .. v0}, Lrandomclass;->getSuperclass()Lsuperclass; - - #the following instructions should be commented out - move-result-object v1 - return-object v1 - - #and now back to our normal programming - :here - return-object v0 .end method .method public static FirstInstructionTest(Lrandomclass;)V 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 index e600f15c..77fb5823 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/AnalyzedInstruction.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/AnalyzedInstruction.java @@ -73,24 +73,10 @@ public class AnalyzedInstruction implements Comparable { */ protected final Instruction originalInstruction; - /** - * A dead instruction is one that is unreachable because it follows an odexed instruction that can't be deodexed - * because it's object register is always null. In the non-odexed code that the odex was generated from, we would - * have technically considered this code reachable and could verify it, even though the instruction that ended up - * being odexed was always null, because we would assume both "paths" out of the instruction are valid - the one - * where execution proceeds normally to the next instruction, and the one where an exception occurs and execution - * either goes to a catch block, or out of the method. - * - * However, in the odexed case, we can't verify the code following an undeodexable instruction because we lack - * the register information from the undeodexable instruction - because we don't know the actual method or field - * that is being accessed. - * - * The undeodexable instruction is guaranteed to throw an NPE, so the following code is effectivetly unreachable. - * Once we detect an undeodexeable instruction, the following code is marked as dead up until a non-dead execution - * path merges in. Additionally, we remove the predecessors/successors of any dead instruction. For example, if - * there is a dead goto instruction, then we would remove the target instruction as a successor, and we would - * also remove the dead goto instruction as a predecessor to the target. + * An analyzed instruction's "deadness" is set during analysis (i.e. MethodAnalyzer.analyzer()). A dead instruction + * is one that the analyzer never reaches. This occurs either with natural "dead code" - code that simply has no + * code path that can ever reach it, or code that follows an odexed instruction that can't be deodexed. */ protected boolean dead = false; 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 index 6592be40..0d5b6a17 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/MethodAnalyzer.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/MethodAnalyzer.java @@ -184,7 +184,7 @@ public class MethodAnalyzer { instructionsToAnalyze.set(successor.instructionIndex); } - BitSet odexedInstructions = new BitSet(instructions.size()); + BitSet undeodexedInstructions = new BitSet(instructions.size()); do { boolean didSomething = false; @@ -199,15 +199,18 @@ public class MethodAnalyzer { instructionToAnalyze.dead = false; try { if (instructionToAnalyze.originalInstruction.opcode.odexOnly()) { + //if we had deodexed an odex instruction in a previous pass, we might have more specific + //register information now, so let's restore the original odexed instruction and + //re-deodex it instructionToAnalyze.restoreOdexedInstruction(); } if (!analyzeInstruction(instructionToAnalyze)) { - odexedInstructions.set(i); + undeodexedInstructions.set(i); continue; } else { didSomething = true; - odexedInstructions.clear(i); + undeodexedInstructions.clear(i); } } catch (ValidationException ex) { this.validationException = ex; @@ -234,32 +237,32 @@ public class MethodAnalyzer { break; } - if (!odexedInstructions.isEmpty()) { - for (int i=odexedInstructions.nextSetBit(0); i>=0; i=odexedInstructions.nextSetBit(i+1)) { + if (!undeodexedInstructions.isEmpty()) { + for (int i=undeodexedInstructions.nextSetBit(0); i>=0; i=undeodexedInstructions.nextSetBit(i+1)) { instructionsToAnalyze.set(i); } } } while (true); - for (int i=odexedInstructions.nextSetBit(0); i>=0; i=odexedInstructions.nextSetBit(i+1)) { + for (int i=0; i=0; i=instructionsToProcess.nextSetBit(i+1)) { - AnalyzedInstruction currentInstruction = instructions.valueAt(i); - instructionsToProcess.clear(i); - - if (currentInstruction.dead) { - continue; - } - - boolean isDead = true; - - for (AnalyzedInstruction predecessor: currentInstruction.predecessors) { - if (!predecessor.dead) { - isDead = false; - break; - } - } - - if (isDead) { - currentInstruction.dead = true; - - for (AnalyzedInstruction successor: currentInstruction.successors) { - instructionsToProcess.set(successor.instructionIndex); - } - } - } - } - } - private void setPostRegisterTypeAndPropagateChanges(AnalyzedInstruction analyzedInstruction, int registerNumber, RegisterType registerType) { @@ -658,6 +620,10 @@ public class MethodAnalyzer { return exceptionHandlers; } + /** + * @return false if analyzedInstruction is an odex instruction that couldn't be deodexed, due to its + * object register being null + */ private boolean analyzeInstruction(AnalyzedInstruction analyzedInstruction) { Instruction instruction = analyzedInstruction.instruction; diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Format.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Format.java index 720de0f9..9ca95852 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Format.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Format.java @@ -62,7 +62,7 @@ public enum Format { ArrayData(null, -1, true), PackedSwitchData(null, -1, true), SparseSwitchData(null, -1, true), - UnresolvedNullReference(null, -1, false), + UnresolvedOdexInstruction(null, -1, false), ; public final Instruction.InstructionFactory Factory; diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/UnresolvedNullReference.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/UnresolvedOdexInstruction.java similarity index 92% rename from dexlib/src/main/java/org/jf/dexlib/Code/Format/UnresolvedNullReference.java rename to dexlib/src/main/java/org/jf/dexlib/Code/Format/UnresolvedOdexInstruction.java index 8c51ce28..00fd5152 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Format/UnresolvedNullReference.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/UnresolvedOdexInstruction.java @@ -36,12 +36,12 @@ import org.jf.dexlib.Util.AnnotatedOutput; * can't be determined. Typically, these are replaced by an equivalent instruction that would have the same * effect (namely, an NPE) */ -public class UnresolvedNullReference extends Instruction { +public class UnresolvedOdexInstruction extends Instruction { public final Instruction OriginalInstruction; //the register number that holds the (null) reference type that the instruction operates on public final int ObjectRegisterNum; - public UnresolvedNullReference(Instruction originalInstruction, int objectRegisterNumber) { + public UnresolvedOdexInstruction(Instruction originalInstruction, int objectRegisterNumber) { super(originalInstruction.opcode); this.OriginalInstruction = originalInstruction; this.ObjectRegisterNum = objectRegisterNumber; @@ -57,6 +57,6 @@ public class UnresolvedNullReference extends Instruction { } public Format getFormat() { - return Format.UnresolvedNullReference; + return Format.UnresolvedOdexInstruction; } }