From c65a8d8b5f2091a6bd835617262840a3f52c5c00 Mon Sep 17 00:00:00 2001 From: "JesusFreke@JesusFreke.com" Date: Thu, 10 Sep 2009 06:40:58 +0000 Subject: [PATCH] Added better support for the case when an optimized instruction can't be resolved due to the object register always being null. It should comment out any dead code following the unresolved instruction (to prevent verification issues, due to the changed method call) git-svn-id: https://smali.googlecode.com/svn/trunk@455 55b6fa8a-2a1e-11de-a435-ffa8d773f76a --- .../jf/baksmali/Adaptors/BlankMethodItem.java | 1 - .../Adaptors/CommentedOutMethodItem.java | 39 ++- .../Adaptors/EndTryLabelMethodItem.java | 2 +- ...=> UnresolvedNullReferenceMethodItem.java} | 10 +- .../jf/baksmali/Adaptors/LabelMethodItem.java | 13 +- .../baksmali/Adaptors/MethodDefinition.java | 247 ++++++++++++------ .../templates/templates/baksmali.stg | 31 ++- .../dexlib/Code/Format/DeadInstruction.java | 26 +- .../org/jf/dexlib/Code/Format/Format.java | 7 +- ...2csn.java => UnresolvedNullReference.java} | 30 +-- .../java/org/jf/dexlib/Util/DeodexUtil.java | 80 +++++- 11 files changed, 325 insertions(+), 161 deletions(-) rename dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35msn.java => baksmali/src/main/java/org/jf/baksmali/Adaptors/CommentedOutMethodItem.java (60%) rename baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/{Instruction22csnMethodItem.java => UnresolvedNullReferenceMethodItem.java} (83%) rename baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/Instruction35msnMethodItem.java => dexlib/src/main/java/org/jf/dexlib/Code/Format/DeadInstruction.java (68%) rename dexlib/src/main/java/org/jf/dexlib/Code/Format/{Instruction22csn.java => UnresolvedNullReference.java} (69%) diff --git a/baksmali/src/main/java/org/jf/baksmali/Adaptors/BlankMethodItem.java b/baksmali/src/main/java/org/jf/baksmali/Adaptors/BlankMethodItem.java index 18d7b0da..f173a515 100644 --- a/baksmali/src/main/java/org/jf/baksmali/Adaptors/BlankMethodItem.java +++ b/baksmali/src/main/java/org/jf/baksmali/Adaptors/BlankMethodItem.java @@ -42,7 +42,6 @@ public class BlankMethodItem extends MethodItem { } } - public int getSortOrder() { return Integer.MAX_VALUE; } diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35msn.java b/baksmali/src/main/java/org/jf/baksmali/Adaptors/CommentedOutMethodItem.java similarity index 60% rename from dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35msn.java rename to baksmali/src/main/java/org/jf/baksmali/Adaptors/CommentedOutMethodItem.java index 71173294..1e362108 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35msn.java +++ b/baksmali/src/main/java/org/jf/baksmali/Adaptors/CommentedOutMethodItem.java @@ -26,32 +26,29 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.jf.dexlib.Code.Format; +package org.jf.baksmali.Adaptors; -import org.jf.dexlib.Code.Opcode; -import org.jf.dexlib.Code.Instruction; +import org.antlr.stringtemplate.StringTemplate; +import org.antlr.stringtemplate.StringTemplateGroup; -/** - * This represents a "fixed" Format35ms, or Format3rms instruction, where the object register is always null and so the - * correct type can't be determined. How this is handled is "implementation dependent". baksmali just replaces it with - * a call to object->hashCode(). Since the object register is always null, this will have the same effect as calling - * whatever the original method was - namely, a NPE - */ -public class Instruction35msn extends Instruction { +public class CommentedOutMethodItem extends MethodItem { + private final StringTemplateGroup stg; + private final MethodItem commentedOutMethodItem; - /** - * this is the first register, i.e. the object register. The others don't matter, because we don't know what - * the original method call is/was. - */ - public final int RegisterNum; - - public Instruction35msn(int registerNum) { - super(Opcode.INVOKE_VIRTUAL); - this.RegisterNum = registerNum; + public CommentedOutMethodItem(StringTemplateGroup stg, MethodItem commentedOutMethodItem) { + super(commentedOutMethodItem.getOffset()); + this.stg = stg; + this.commentedOutMethodItem = commentedOutMethodItem; } + public int getSortOrder() { + return commentedOutMethodItem.getSortOrder() + 1; + } - public Format getFormat() { - return Format.Format35msn; + @Override + public String toString() { + StringTemplate template = stg.getInstanceOf("CommentedOutMethodItem"); + template.setAttribute("MethodItem", commentedOutMethodItem); + return template.toString(); } } diff --git a/baksmali/src/main/java/org/jf/baksmali/Adaptors/EndTryLabelMethodItem.java b/baksmali/src/main/java/org/jf/baksmali/Adaptors/EndTryLabelMethodItem.java index 2298fdde..403c4832 100644 --- a/baksmali/src/main/java/org/jf/baksmali/Adaptors/EndTryLabelMethodItem.java +++ b/baksmali/src/main/java/org/jf/baksmali/Adaptors/EndTryLabelMethodItem.java @@ -34,7 +34,7 @@ public class EndTryLabelMethodItem extends LabelMethodItem { private int labelOffset; public EndTryLabelMethodItem(int offset, StringTemplateGroup stg, int labelOffset) { - super(offset, stg, "try_end_"); + super(offset, stg, "try_end_", false); this.labelOffset = labelOffset; } diff --git a/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/Instruction22csnMethodItem.java b/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/UnresolvedNullReferenceMethodItem.java similarity index 83% rename from baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/Instruction22csnMethodItem.java rename to baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/UnresolvedNullReferenceMethodItem.java index e5bacc74..aea5f7be 100644 --- a/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/Instruction22csnMethodItem.java +++ b/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/UnresolvedNullReferenceMethodItem.java @@ -28,18 +28,18 @@ package org.jf.baksmali.Adaptors.Format; -import org.jf.dexlib.Code.Format.Instruction22csn; +import org.jf.dexlib.Code.Format.UnresolvedNullReference; import org.jf.dexlib.CodeItem; import org.antlr.stringtemplate.StringTemplateGroup; import org.antlr.stringtemplate.StringTemplate; -public class Instruction22csnMethodItem extends InstructionFormatMethodItem { - public Instruction22csnMethodItem(CodeItem codeItem, int offset, StringTemplateGroup stg, - Instruction22csn instruction) { +public class UnresolvedNullReferenceMethodItem extends InstructionFormatMethodItem { + public UnresolvedNullReferenceMethodItem(CodeItem codeItem, int offset, StringTemplateGroup stg, + UnresolvedNullReference instruction) { super(codeItem, offset, stg, instruction); } protected void setAttributes(StringTemplate template) { - template.setAttribute("Register", formatRegister(instruction.RegisterNum)); + template.setAttribute("Register", formatRegister(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 d9866e6c..be2f7ab5 100644 --- a/baksmali/src/main/java/org/jf/baksmali/Adaptors/LabelMethodItem.java +++ b/baksmali/src/main/java/org/jf/baksmali/Adaptors/LabelMethodItem.java @@ -34,17 +34,27 @@ import org.antlr.stringtemplate.StringTemplate; public class LabelMethodItem extends MethodItem { private final StringTemplateGroup stg; private final String labelPrefix; + private boolean isCommentedOut = false; - public LabelMethodItem(int offset, StringTemplateGroup stg, String labelPrefix) { + public LabelMethodItem(int offset, StringTemplateGroup stg, String labelPrefix, boolean isCommentedOut) { super(offset); this.stg = stg; this.labelPrefix = labelPrefix; + this.isCommentedOut = isCommentedOut; } public int getSortOrder() { return 0; } + public boolean isCommentedOut() { + return isCommentedOut; + } + + public void setCommentedOut(boolean isCommentedOut) { + this.isCommentedOut = isCommentedOut; + } + public int compareTo(MethodItem methodItem) { int result = super.compareTo(methodItem); @@ -71,6 +81,7 @@ public class LabelMethodItem extends MethodItem { @Override public String toString() { StringTemplate template = stg.getInstanceOf("Label"); + template.setAttribute("CommentedOut", this.isCommentedOut); template.setAttribute("Prefix", labelPrefix); template.setAttribute("HexOffset", getLabelOffset()); return template.toString(); 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 7b9b3ac5..7564825e 100644 --- a/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java +++ b/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java @@ -143,7 +143,7 @@ public class MethodDefinition { MethodItemList methodItemList = new MethodItemList(dexFile, stg, codeItem); methodItemList.generateMethodItemList(); - methodItems.addAll(methodItemList.labels); + methodItems.addAll(methodItemList.labels.values()); methodItems.addAll(methodItemList.instructions); methodItems.addAll(methodItemList.blanks); methodItems.addAll(methodItemList.catches); @@ -159,7 +159,7 @@ public class MethodDefinition { private final StringTemplateGroup stg; private final CodeItem codeItem; - public HashSet labels = new HashSet(); + public HashMap labels = new HashMap(); public List instructions = new ArrayList(); public List blanks = new ArrayList(); public List catches = new ArrayList(); @@ -197,7 +197,7 @@ public class MethodDefinition { offset = 0; for (Instruction instruction: instructions) { - addMethodItemsForInstruction(offset, instruction); + addMethodItemsForInstruction(offset, instruction, false, null); blanks.add(new BlankMethodItem(stg, offset)); offset += instruction.getSize()/2; @@ -237,7 +237,7 @@ public class MethodDefinition { new InstructionIterator.ProcessInstructionDelegate() { public void ProcessInstruction(int index, Instruction instruction) { int offset = index/2; - addMethodItemsForInstruction(offset, instruction); + addMethodItemsForInstruction(offset, instruction, false, null); blanks.add(new BlankMethodItem(stg, offset)); } }); @@ -250,136 +250,213 @@ public class MethodDefinition { addDebugInfo(); } - private void addMethodItemsForInstruction(int offset, Instruction instruction) { + private void addInstructionMethodItem(InstructionFormatMethodItem methodItem, boolean commentedOut, + String comment) { + if (commentedOut) { + instructions.add(new CommentedOutMethodItem(stg, methodItem)); + } else { + instructions.add(methodItem); + } + } + + private void addLabelMethodItem(LabelMethodItem labelMethodItem) { + LabelMethodItem internedLabelMethodItem = labels.get(labelMethodItem); + if (internedLabelMethodItem != null) { + if (!labelMethodItem.isCommentedOut() && internedLabelMethodItem.isCommentedOut()) { + internedLabelMethodItem.setCommentedOut(false); + } + } else { + labels.put(labelMethodItem, labelMethodItem); + } + } + + private void addMethodItemsForInstruction(int offset, Instruction instruction, boolean commentedOut, + String comment) { switch (instruction.getFormat()) { case Format10t: - instructions.add(new Instruction10tMethodItem(codeItem, offset, stg,(Instruction10t)instruction)); - labels.add(new LabelMethodItem(offset + ((Instruction10t)instruction).getOffset(), stg, "goto_")); + addInstructionMethodItem( + new Instruction10tMethodItem(codeItem, offset, stg,(Instruction10t)instruction), + commentedOut, comment); + addLabelMethodItem(new LabelMethodItem(offset + ((Instruction10t)instruction).getOffset(), stg, + "goto_", commentedOut)); return; case Format10x: - instructions.add(new Instruction10xMethodItem(codeItem, offset, stg, (Instruction10x)instruction)); + addInstructionMethodItem( + new Instruction10xMethodItem(codeItem, offset, stg, (Instruction10x)instruction), + commentedOut, comment); return; case Format11n: - instructions.add(new Instruction11nMethodItem(codeItem, offset, stg, (Instruction11n)instruction)); + addInstructionMethodItem( + new Instruction11nMethodItem(codeItem, offset, stg, (Instruction11n)instruction), + commentedOut, comment); return; case Format11x: - instructions.add(new Instruction11xMethodItem(codeItem, offset, stg, (Instruction11x)instruction)); + addInstructionMethodItem( + new Instruction11xMethodItem(codeItem, offset, stg, (Instruction11x)instruction), + commentedOut, comment); return; case Format12x: - instructions.add(new Instruction12xMethodItem(codeItem, offset, stg, (Instruction12x)instruction)); + addInstructionMethodItem( + new Instruction12xMethodItem(codeItem, offset, stg, (Instruction12x)instruction), + commentedOut, comment); return; case Format20t: - instructions.add(new Instruction20tMethodItem(codeItem, offset, stg, (Instruction20t)instruction)); - labels.add(new LabelMethodItem(offset + ((Instruction20t)instruction).getOffset(), stg, "goto_")); + addInstructionMethodItem( + new Instruction20tMethodItem(codeItem, offset, stg, (Instruction20t)instruction), + commentedOut, comment); + addLabelMethodItem(new LabelMethodItem(offset + ((Instruction20t)instruction).getOffset(), stg, + "goto_", commentedOut)); return; case Format21c: - instructions.add(new Instruction21cMethodItem(codeItem, offset, stg, (Instruction21c)instruction)); + addInstructionMethodItem( + new Instruction21cMethodItem(codeItem, offset, stg, (Instruction21c)instruction), + commentedOut, comment); return; case Format21h: - instructions.add(new Instruction21hMethodItem(codeItem, offset, stg, (Instruction21h)instruction)); + addInstructionMethodItem( + new Instruction21hMethodItem(codeItem, offset, stg, (Instruction21h)instruction), + commentedOut, comment); return; case Format21s: - instructions.add(new Instruction21sMethodItem(codeItem, offset, stg, (Instruction21s)instruction)); + addInstructionMethodItem( + new Instruction21sMethodItem(codeItem, offset, stg, (Instruction21s)instruction), + commentedOut, comment); return; case Format21t: - instructions.add(new Instruction21tMethodItem(codeItem, offset, stg, (Instruction21t)instruction)); - labels.add(new LabelMethodItem(offset + ((Instruction21t)instruction).getOffset(), stg, "cond_")); + addInstructionMethodItem( + new Instruction21tMethodItem(codeItem, offset, stg, (Instruction21t)instruction), + commentedOut, comment); + addLabelMethodItem(new LabelMethodItem(offset + ((Instruction21t)instruction).getOffset(), stg, + "cond_", commentedOut)); return; case Format22b: - instructions.add(new Instruction22bMethodItem(codeItem, offset, stg, (Instruction22b)instruction)); + addInstructionMethodItem( + new Instruction22bMethodItem(codeItem, offset, stg, (Instruction22b)instruction), + commentedOut, comment); return; case Format22c: - instructions.add(new Instruction22cMethodItem(codeItem, offset, stg, (Instruction22c)instruction)); + addInstructionMethodItem( + new Instruction22cMethodItem(codeItem, offset, stg, (Instruction22c)instruction), + commentedOut, comment); return; case Format22cs: - instructions.add(new Instruction22csMethodItem(codeItem, offset, stg, - (Instruction22cs)instruction)); + addInstructionMethodItem( + new Instruction22csMethodItem(codeItem, offset, stg, (Instruction22cs)instruction), + commentedOut, comment); return; case Format22csf: - instructions.add(new Instruction22csfMethodItem(codeItem, offset, stg, - (Instruction22csf)instruction)); - return; - case Format22csn: - instructions.add(new Instruction22csnMethodItem(codeItem, offset, stg, - (Instruction22csn)instruction)); + addInstructionMethodItem( + new Instruction22csfMethodItem(codeItem, offset, stg, (Instruction22csf)instruction), + commentedOut, comment); return; case Format22s: - instructions.add(new Instruction22sMethodItem(codeItem, offset, stg, (Instruction22s)instruction)); + addInstructionMethodItem( + new Instruction22sMethodItem(codeItem, offset, stg, (Instruction22s)instruction), + commentedOut, comment); return; case Format22t: - instructions.add(new Instruction22tMethodItem(codeItem, offset, stg, (Instruction22t)instruction)); - labels.add(new LabelMethodItem(offset + ((Instruction22t)instruction).getOffset(), stg, "cond_")); + addInstructionMethodItem( + new Instruction22tMethodItem(codeItem, offset, stg, (Instruction22t)instruction), + commentedOut, comment); + addLabelMethodItem(new LabelMethodItem(offset + ((Instruction22t)instruction).getOffset(), stg, + "cond_", commentedOut)); return; case Format22x: - instructions.add(new Instruction22xMethodItem(codeItem, offset, stg, (Instruction22x)instruction)); + addInstructionMethodItem( + new Instruction22xMethodItem(codeItem, offset, stg, (Instruction22x)instruction), + commentedOut, comment); return; case Format23x: - instructions.add(new Instruction23xMethodItem(codeItem, offset, stg, (Instruction23x)instruction)); + addInstructionMethodItem( + new Instruction23xMethodItem(codeItem, offset, stg, (Instruction23x)instruction), + commentedOut, comment); return; case Format30t: - instructions.add(new Instruction30tMethodItem(codeItem, offset, stg, (Instruction30t)instruction)); - labels.add(new LabelMethodItem(offset + ((Instruction30t)instruction).getOffset(), stg, "goto_")); + addInstructionMethodItem( + new Instruction30tMethodItem(codeItem, offset, stg, (Instruction30t)instruction), + commentedOut, comment); + addLabelMethodItem(new LabelMethodItem(offset + ((Instruction30t)instruction).getOffset(), stg, + "goto_", commentedOut)); return; case Format31c: - instructions.add(new Instruction31cMethodItem(codeItem, offset, stg, (Instruction31c)instruction)); + addInstructionMethodItem( + new Instruction31cMethodItem(codeItem, offset, stg, (Instruction31c)instruction), + commentedOut, comment); return; case Format31i: - instructions.add(new Instruction31iMethodItem(codeItem, offset, stg, (Instruction31i)instruction)); + addInstructionMethodItem( + new Instruction31iMethodItem(codeItem, offset, stg, (Instruction31i)instruction), + commentedOut, comment); return; case Format31t: - instructions.add(new Instruction31tMethodItem(codeItem, offset, stg, (Instruction31t)instruction)); + addInstructionMethodItem( + new Instruction31tMethodItem(codeItem, offset, stg, (Instruction31t)instruction), + commentedOut, comment); if (instruction.opcode == Opcode.FILL_ARRAY_DATA) { - labels.add(new LabelMethodItem(offset + ((Instruction31t)instruction).getOffset(), stg, - "array_")); + addLabelMethodItem(new LabelMethodItem(offset + ((Instruction31t)instruction).getOffset(), stg, + "array_", commentedOut)); } else if (instruction.opcode == Opcode.PACKED_SWITCH) { - labels.add(new LabelMethodItem(offset + ((Instruction31t)instruction).getOffset(), stg, - "pswitch_data_")); + addLabelMethodItem(new LabelMethodItem(offset + ((Instruction31t)instruction).getOffset(), stg, + "pswitch_data_", commentedOut)); } else if (instruction.opcode == Opcode.SPARSE_SWITCH) { - labels.add(new LabelMethodItem(offset + ((Instruction31t)instruction).getOffset(), stg, - "sswitch_data_")); + addLabelMethodItem(new LabelMethodItem(offset + ((Instruction31t)instruction).getOffset(), stg, + "sswitch_data_", commentedOut)); } return; case Format32x: - instructions.add(new Instruction32xMethodItem(codeItem, offset, stg, (Instruction32x)instruction)); + addInstructionMethodItem( + new Instruction32xMethodItem(codeItem, offset, stg, (Instruction32x)instruction), + commentedOut, comment); return; case Format35c: - instructions.add(new Instruction35cMethodItem(codeItem, offset, stg, (Instruction35c)instruction)); + addInstructionMethodItem( + new Instruction35cMethodItem(codeItem, offset, stg, (Instruction35c)instruction), + commentedOut, comment); return; case Format35s: - instructions.add(new Instruction35sMethodItem(codeItem, offset, stg, (Instruction35s)instruction)); + addInstructionMethodItem( + new Instruction35sMethodItem(codeItem, offset, stg, (Instruction35s)instruction), + commentedOut, comment); return; case Format35sf: - instructions.add(new Instruction35sfMethodItem(codeItem, offset, stg, - (Instruction35sf)instruction)); + addInstructionMethodItem( + new Instruction35sfMethodItem(codeItem, offset, stg, (Instruction35sf)instruction), + commentedOut, comment); return; case Format35ms: - instructions.add(new Instruction35msMethodItem(codeItem, offset, stg, - (Instruction35ms)instruction)); + addInstructionMethodItem( + new Instruction35msMethodItem(codeItem, offset, stg, (Instruction35ms)instruction), + commentedOut, comment); return; case Format35msf: - instructions.add(new Instruction35msfMethodItem(codeItem, offset, stg, - (Instruction35msf)instruction)); - return; - case Format35msn: - instructions.add(new Instruction35msnMethodItem(codeItem, offset, stg, - (Instruction35msn)instruction)); + addInstructionMethodItem( + new Instruction35msfMethodItem(codeItem, offset, stg, (Instruction35msf)instruction), + commentedOut, comment); return; case Format3rc: - instructions.add(new Instruction3rcMethodItem(codeItem, offset, stg, (Instruction3rc)instruction)); + addInstructionMethodItem( + new Instruction3rcMethodItem(codeItem, offset, stg, (Instruction3rc)instruction), + commentedOut, comment); return; case Format3rms: - instructions.add(new Instruction3rmsMethodItem(codeItem, offset, stg, - (Instruction3rms)instruction)); + addInstructionMethodItem( + new Instruction3rmsMethodItem(codeItem, offset, stg, (Instruction3rms)instruction), + commentedOut, comment); return; case Format3rmsf: - instructions.add(new Instruction3rmsfMethodItem(codeItem, offset, stg, - (Instruction3rmsf)instruction)); + addInstructionMethodItem( + new Instruction3rmsfMethodItem(codeItem, offset, stg, (Instruction3rmsf)instruction), + commentedOut, comment); return; case Format51l: - instructions.add(new Instruction51lMethodItem(codeItem, offset, stg, (Instruction51l)instruction)); + addInstructionMethodItem( + new Instruction51lMethodItem(codeItem, offset, stg, (Instruction51l)instruction), + commentedOut, comment); return; case ArrayData: - instructions.add(new ArrayDataMethodItem(codeItem, offset, stg, (ArrayDataPseudoInstruction)instruction)); + addInstructionMethodItem( + new ArrayDataMethodItem(codeItem, offset, stg, (ArrayDataPseudoInstruction)instruction), + commentedOut, comment); return; case PackedSwitchData: { @@ -389,14 +466,16 @@ public class MethodDefinition { PackedSwitchDataPseudoInstruction packedSwitchInstruction = (PackedSwitchDataPseudoInstruction)instruction; - instructions.add(new PackedSwitchMethodItem(codeItem, offset, stg, - packedSwitchInstruction, baseAddress)); + addInstructionMethodItem( + new PackedSwitchMethodItem(codeItem, offset, stg, packedSwitchInstruction, baseAddress), + commentedOut, comment); Iterator iterator = packedSwitchInstruction.getTargets(); while (iterator.hasNext()) { PackedSwitchDataPseudoInstruction.PackedSwitchTarget target = iterator.next(); - labels.add(new LabelMethodItem(baseAddress + target.target, stg, "pswitch_")); + addLabelMethodItem(new LabelMethodItem(baseAddress + target.target, stg, "pswitch_", + commentedOut)); } } return; @@ -409,16 +488,32 @@ public class MethodDefinition { SparseSwitchDataPseudoInstruction sparseSwitchInstruction = (SparseSwitchDataPseudoInstruction)instruction; - instructions.add(new SparseSwitchMethodItem(codeItem, offset, stg, - sparseSwitchInstruction, baseAddress)); + addInstructionMethodItem( + new SparseSwitchMethodItem(codeItem, offset, stg, sparseSwitchInstruction, baseAddress), + commentedOut, comment); Iterator iterator = sparseSwitchInstruction.getTargets(); while (iterator.hasNext()) { SparseSwitchDataPseudoInstruction.SparseSwitchTarget target = iterator.next(); - labels.add(new LabelMethodItem(baseAddress + target.target, stg, "sswitch_")); + addLabelMethodItem(new LabelMethodItem(baseAddress + target.target, stg, "sswitch_", + commentedOut)); } } + return; + } + case UnresolvedNullReference: + { + addInstructionMethodItem(new UnresolvedNullReferenceMethodItem(codeItem, offset, stg, + (UnresolvedNullReference)instruction), commentedOut, comment); + addMethodItemsForInstruction(offset, ((UnresolvedNullReference)instruction).OriginalInstruction, + true, null); + return; + } + case DeadInstruction: + { + addMethodItemsForInstruction(offset, ((DeadInstruction)instruction).OriginalInstruction, true, null); + return; } } } @@ -471,11 +566,11 @@ public class MethodDefinition { }; catches.add(catchMethodItem); - labels.add(new LabelMethodItem(startAddress, stg, "try_start_")); + addLabelMethodItem(new LabelMethodItem(startAddress, stg, "try_start_", false)); //use the offset from the last covered instruction, but make the label //name refer to the address of the next instruction - labels.add(new EndTryLabelMethodItem(lastInstructionOffset, stg, endAddress)); - labels.add(new LabelMethodItem(catchAllAddress, stg, "handler_")); + addLabelMethodItem(new EndTryLabelMethodItem(lastInstructionOffset, stg, endAddress)); + addLabelMethodItem(new LabelMethodItem(catchAllAddress, stg, "handler_", false)); } @@ -487,11 +582,11 @@ public class MethodDefinition { handler.exceptionType, startAddress, endAddress, handler.handlerAddress); catches.add(catchMethodItem); - labels.add(new LabelMethodItem(startAddress, stg, "try_start_")); + addLabelMethodItem(new LabelMethodItem(startAddress, stg, "try_start_", false)); //use the offset from the last covered instruction, but make the label //name refer to the address of the next instruction - labels.add(new EndTryLabelMethodItem(lastInstructionOffset, stg, endAddress)); - labels.add(new LabelMethodItem(handler.handlerAddress, stg, "handler_")); + addLabelMethodItem(new EndTryLabelMethodItem(lastInstructionOffset, stg, endAddress)); + addLabelMethodItem(new LabelMethodItem(handler.handlerAddress, stg, "handler_", false)); } } } diff --git a/baksmali/src/main/resources/templates/templates/baksmali.stg b/baksmali/src/main/resources/templates/templates/baksmali.stg index 6dd519ee..62b7346e 100644 --- a/baksmali/src/main/resources/templates/templates/baksmali.stg +++ b/baksmali/src/main/resources/templates/templates/baksmali.stg @@ -198,12 +198,6 @@ Format22csf(Opcode, RegisterA, RegisterB, Reference) ::= , , >> -Format22csn(Opcode, Register) ::= -<< -#Couldn't determine the field while deodexing, replaced with generic method call -invoke-virtual/range { .. }, Ljava/lang/Object;->hashCode()I ->> - Format22s(Opcode, RegisterA, RegisterB, Literal) ::= << , , @@ -274,12 +268,6 @@ Format35msf(Opcode, Registers, Reference) ::= {}, >> -Format35msn(Opcode, Register) ::= -<< -#Couldn't determine method while deodexing, replaced with generic method call -invoke-virtual/range { .. }, Ljava/lang/Object;->hashCode()I ->> - Format3rc(Opcode, StartRegister, LastRegister, Reference) ::= << { .. }, @@ -300,6 +288,21 @@ Format51l(Opcode, Register, Literal) ::= , >> +CommentedOutMethodItem(MethodItem) ::= +<< +# +>> + +UnresolvedNullReference(Opcode, Register) ::= +<< +#Couldn't resolve optimized instruction while deodexing, due to the object register +#always being null. Replaced with generic method call +invoke-virtual/range { .. }, Ljava/lang/Object;->hashCode()I +goto/32 0 +>> + + + ArrayData(Opcode, ElementWidth, Values) ::= << .array-data @@ -327,9 +330,9 @@ SparseSwitchData(Opcode, Targets) ::= >> -Label(Prefix, HexOffset) ::= +Label(Prefix, HexOffset, CommentedOut) ::= << -: +#: >> Line(Line) ::= diff --git a/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/Instruction35msnMethodItem.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/DeadInstruction.java similarity index 68% rename from baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/Instruction35msnMethodItem.java rename to dexlib/src/main/java/org/jf/dexlib/Code/Format/DeadInstruction.java index 747ddf83..d66b1f93 100644 --- a/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/Instruction35msnMethodItem.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/DeadInstruction.java @@ -26,20 +26,24 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.jf.baksmali.Adaptors.Format; +package org.jf.dexlib.Code.Format; -import org.jf.dexlib.Code.Format.Instruction35msn; -import org.jf.dexlib.CodeItem; -import org.antlr.stringtemplate.StringTemplateGroup; -import org.antlr.stringtemplate.StringTemplate; +import org.jf.dexlib.Code.Instruction; -public class Instruction35msnMethodItem extends InstructionFormatMethodItem { - public Instruction35msnMethodItem(CodeItem codeItem, int offset, StringTemplateGroup stg, - Instruction35msn instruction) { - super(codeItem, offset, stg, instruction); +public class DeadInstruction extends Instruction { + public final Instruction OriginalInstruction; + + public DeadInstruction(Instruction originalInstruction) { + super(originalInstruction.opcode); + this.OriginalInstruction = originalInstruction; } - protected void setAttributes(StringTemplate template) { - template.setAttribute("Register", formatRegister(instruction.RegisterNum)); + @Override + public int getSize() { + return OriginalInstruction.getSize(); + } + + public Format getFormat() { + return Format.DeadInstruction; } } 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 5c36d062..61976876 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 @@ -45,7 +45,6 @@ public enum Format { Format22c(Instruction22c.Factory, 4), Format22cs(Instruction22cs.Factory, 4), Format22csf(null, 4), - Format22csn(null, 4), Format22s(Instruction22s.Factory, 4), Format22t(Instruction22t.Factory, 4), Format22x(Instruction22x.Factory, 4), @@ -60,14 +59,16 @@ public enum Format { Format35sf(null, 6), Format35ms(Instruction35ms.Factory, 6), Format35msf(null, 6), - Format35msn(null, 6), Format3rc(Instruction3rc.Factory, 6), Format3rms(Instruction3rms.Factory, 6), Format3rmsf(null, 6), Format51l(Instruction51l.Factory, 10), ArrayData(null, -1, true), PackedSwitchData(null, -1, true), - SparseSwitchData(null, -1, true); + SparseSwitchData(null, -1, true), + UnresolvedNullReference(null, -1, false), + DeadInstruction(null, -1, false) + ; public final Instruction.InstructionFactory Factory; public final int size; diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22csn.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/UnresolvedNullReference.java similarity index 69% rename from dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22csn.java rename to dexlib/src/main/java/org/jf/dexlib/Code/Format/UnresolvedNullReference.java index e9a94c25..8d71b253 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22csn.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/UnresolvedNullReference.java @@ -29,30 +29,30 @@ package org.jf.dexlib.Code.Format; import org.jf.dexlib.Code.Instruction; -import org.jf.dexlib.Code.Opcode; /** - * This represents a "fixed" Format22cs instruction, where the object register is always null and so the correct type + * This represents a "fixed" odexed instruction, where the object register is always null and so the correct type * can't be determined. How this is handled is "implementation dependent". baksmali just replaces it with a call to * object->hashCode(). Since the object register is always null, this will have the same effect as tring to access - * whatever field that was trying to be accessed - namely, a NPE + * whatever method/field that was trying to be accessed - namely, a NPE */ -public class Instruction22csn extends Instruction { +public class UnresolvedNullReference extends Instruction { + public final Instruction OriginalInstruction; + //the register number that holds the (null) reference type that the instruction operates on + public final int ObjectRegisterNum; - /** - * this is the first register, i.e. the object register. The others don't matter, because we don't know what - * the original field is/was. - */ - public final int RegisterNum; - - public Instruction22csn(int registerNum) { - //the opcode should be ignored. It just needs to be a 4 byte opcode - super(Opcode.IGET_QUICK); - this.RegisterNum = registerNum; + public UnresolvedNullReference(Instruction originalInstruction, int objectRegisterNumber) { + super(originalInstruction.opcode); + this.OriginalInstruction = originalInstruction; + this.ObjectRegisterNum = objectRegisterNumber; } + @Override + public int getSize() { + return OriginalInstruction.getSize(); + } public Format getFormat() { - return Format.Format35msn; + return Format.UnresolvedNullReference; } } diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/DeodexUtil.java b/dexlib/src/main/java/org/jf/dexlib/Util/DeodexUtil.java index fea02e1b..2aa960f5 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Util/DeodexUtil.java +++ b/dexlib/src/main/java/org/jf/dexlib/Util/DeodexUtil.java @@ -205,7 +205,9 @@ public class DeodexUtil { if (deodexInstruction(i)) { didSomething = true; } else { - somethingLeftToDo = true; + if (!i.dead) { + somethingLeftToDo = true; + } } } } @@ -219,7 +221,13 @@ public class DeodexUtil { List instructions = new ArrayList(insns.size()); for (insn i: insns) { - if (i.instruction.opcode.odexOnly) { + if (i.dead) { + if (i.fixedInstruction != null) { + instructions.add(new DeadInstruction(i.fixedInstruction)); + } else { + instructions.add(new DeadInstruction(i.instruction)); + } + } else if (i.instruction.opcode.odexOnly) { assert i.fixedInstruction != null; instructions.add(i.fixedInstruction); } else { @@ -289,7 +297,7 @@ public class DeodexUtil { //We actually just create an Instruction2csn, which doesn't have any method/field info associated with //it, and let the caller choose which "default" method to call in this case if (regType == RegisterType.Null) { - i.fixedInstruction = new Instruction22csn(registerNum); + i.fixedInstruction = new UnresolvedNullReference(i.instruction, registerNum); return true; } @@ -351,7 +359,7 @@ public class DeodexUtil { //We actually just create an Instruction2csn, which doesn't have any method/field info associated with //it, and let the caller choose which "default" method to call in this case if (regType == RegisterType.Null) { - i.fixedInstruction = new Instruction22csn(registerNum); + i.fixedInstruction = new UnresolvedNullReference(i.instruction, registerNum); return true; } @@ -392,7 +400,7 @@ public class DeodexUtil { //We actually just create an Instruction2csn, which doesn't have any method/field info associated with //it, and let the caller choose which "default" method to call in this case if (regType == RegisterType.Null) { - i.fixedInstruction = new Instruction22csn(registerNum); + i.fixedInstruction = new UnresolvedNullReference(i.instruction, registerNum); return true; } @@ -435,7 +443,7 @@ public class DeodexUtil { //We actually just create an Instruction2csn, which doesn't have any method/field info associated with //it, and let the caller choose which "default" method to call in this case if (regType == RegisterType.Null) { - i.fixedInstruction = new Instruction22csn(registerNum); + i.fixedInstruction = new UnresolvedNullReference(i.instruction, registerNum); return true; } @@ -497,7 +505,7 @@ public class DeodexUtil { //We actually just create an Instruction2csn, which doesn't have any method/field info associated with //it, and let the caller choose which "default" method to call in this case if (regType == RegisterType.Null) { - i.fixedInstruction = new Instruction22csn(registerNum); + i.fixedInstruction = new UnresolvedNullReference(i.instruction, registerNum); return true; } @@ -539,7 +547,7 @@ public class DeodexUtil { //We actually just create an Instruction2csn, which doesn't have any method/field info associated with //it, and let the caller choose which "default" method to call in this case if (regType == RegisterType.Null) { - i.fixedInstruction = new Instruction22csn(registerNum); + i.fixedInstruction = new UnresolvedNullReference(i.instruction, registerNum); return true; } @@ -581,7 +589,8 @@ public class DeodexUtil { //We actually just create an Instruction3msn, which doesn't have any method info associated with it, //and let the caller choose which "default" method to call in this case if (regType == RegisterType.Null) { - i.fixedInstruction = new Instruction35msn(registerNum); + i.fixedInstruction = new UnresolvedNullReference(i.instruction, registerNum); + i.propogateDeadness(); return true; } @@ -627,7 +636,7 @@ public class DeodexUtil { //We actually just create an Instruction3msn, which doesn't have any method info associated with it, //and let the caller choose which "default" method to call in this case if (regType == RegisterType.Null) { - i.fixedInstruction = new Instruction35msn(registerNum); + i.fixedInstruction = new UnresolvedNullReference(i.instruction, registerNum); return true; } @@ -673,7 +682,9 @@ public class DeodexUtil { //We actually just create an Instruction3msn, which doesn't have any method info associated with it, //and let the caller choose which "default" method to call in this case if (regType == RegisterType.Null) { - i.fixedInstruction = new Instruction35msn(registerNum); + i.fixedInstruction = new UnresolvedNullReference(i.instruction, registerNum); + //we need to mark any following instructions as dead + i.propogateDeadness(); return true; } @@ -719,7 +730,7 @@ public class DeodexUtil { //We actually just create an Instruction3msn, which doesn't have any method info associated with it, //and let the caller choose which "default" method to call in this case if (regType == RegisterType.Null) { - i.fixedInstruction = new Instruction35msn(registerNum); + i.fixedInstruction = new UnresolvedNullReference(i.instruction, registerNum); return true; } @@ -804,6 +815,11 @@ public class DeodexUtil { */ public LinkedList successors = new LinkedList(); + /** + * Instructions that can pass on execution to this one + */ + public LinkedList predecessors = new LinkedList(); + /** * If this instruction is in a try block, these are the first instructions for each * exception handler @@ -860,6 +876,12 @@ public class DeodexUtil { */ public boolean fixed = false; + /** + * If this code is dead. Note that not all dead code is marked. Only the dead code that comes after an odexed + * instruction can't be resolved because its object register is always null. + */ + public boolean dead = false; + public final RegisterType[] registerMap; public final String[] registerTypes; @@ -1246,6 +1268,7 @@ public class DeodexUtil { private void addSuccessor(insn i) { successors.add(i); + i.predecessors.add(this); //if the next instruction can throw an exception, and is covered by exception handlers, //then the execution can in effect go directly from this instruction into the handler @@ -1332,6 +1355,38 @@ public class DeodexUtil { this.propogateRegisters(); } + /** + * This is initially called when this instruction is an odexed instruction that can't be resolved because + * the object register is always null. For each of its successor instructions, it checks if all of the other + * predecessors of that successor are also "dead". If so, it marks it as dead and recursively calls + * propogateDeadness on it. The effect is that all the code that follows after the initial unresolved + * instruction is marked dead, until a non-dead code path merges with the dead code path (and it becomes... + * undead. mmMMmmMMmm brains) + */ + public void propogateDeadness() { + for (insn successor: successors) { + //the first instruction of the method (or the first instruction of any exception handlers covering + //the first instruction) can never be dead + if (successor.firstInstruction) { + continue; + } + + boolean allSucessorsPredecessorsDead = true; + for (insn successorPredecessor: successor.predecessors) { + if (successorPredecessor != this) { + if (!successorPredecessor.dead) { + allSucessorsPredecessorsDead = false; + break; + } + } + } + if (allSucessorsPredecessorsDead) { + successor.dead = true; + successor.propogateDeadness(); + } + } + } + public void propogateRegisters() { visited = true; @@ -1399,7 +1454,6 @@ public class DeodexUtil { } } else { String type = destRegisterType(); - String nextType = nextInsn.registerTypes[registerNum]; if (type != null && !type.equals(nextInsn.registerTypes[registerNum])) { //see comment above for loop