diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/DeodexUtil.java b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/DeodexUtil.java index f649ce2f..2859f2a3 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/DeodexUtil.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/DeodexUtil.java @@ -39,43 +39,7 @@ public class DeodexUtil { public static final int Direct = 1; public static final int Static = 2; - private final InlineMethod[] inlineMethods; - - private final InlineMethod[] inlineMethods_35 = new InlineMethod[] { - new InlineMethod(Static, "Lorg/apache/harmony/dalvik/NativeTestTarget;", "emptyInlineMethod", "", "V"), - new InlineMethod(Virtual, "Ljava/lang/String;", "charAt", "I", "C"), - new InlineMethod(Virtual, "Ljava/lang/String;", "compareTo", "Ljava/lang/String;", "I"), - new InlineMethod(Virtual, "Ljava/lang/String;", "equals", "Ljava/lang/Object;", "Z"), - new InlineMethod(Virtual, "Ljava/lang/String;", "length", "", "I"), - new InlineMethod(Static, "Ljava/lang/Math;", "abs", "I", "I"), - new InlineMethod(Static, "Ljava/lang/Math;", "abs", "J", "J"), - new InlineMethod(Static, "Ljava/lang/Math;", "abs", "F", "F"), - new InlineMethod(Static, "Ljava/lang/Math;", "abs", "D", "D"), - new InlineMethod(Static, "Ljava/lang/Math;", "min", "II", "I"), - new InlineMethod(Static, "Ljava/lang/Math;", "max", "II", "I"), - new InlineMethod(Static, "Ljava/lang/Math;", "sqrt", "D", "D"), - new InlineMethod(Static, "Ljava/lang/Math;", "cos", "D", "D"), - new InlineMethod(Static, "Ljava/lang/Math;", "sin", "D", "D") - }; - - private final InlineMethod[] inlineMethods_36 = new InlineMethod[] { - new InlineMethod(Static, "Lorg/apache/harmony/dalvik/NativeTestTarget;", "emptyInlineMethod", "", "V"), - new InlineMethod(Virtual, "Ljava/lang/String;", "charAt", "I", "C"), - new InlineMethod(Virtual, "Ljava/lang/String;", "compareTo", "Ljava/lang/String;", "I"), - new InlineMethod(Virtual, "Ljava/lang/String;", "equals", "Ljava/lang/Object;", "Z"), - new InlineMethod(Virtual, "Ljava/lang/String;", "indexOf", "I", "I"), - new InlineMethod(Virtual, "Ljava/lang/String;", "indexOf", "II", "I"), - new InlineMethod(Virtual, "Ljava/lang/String;", "length", "", "I"), - new InlineMethod(Static, "Ljava/lang/Math;", "abs", "I", "I"), - new InlineMethod(Static, "Ljava/lang/Math;", "abs", "J", "J"), - new InlineMethod(Static, "Ljava/lang/Math;", "abs", "F", "F"), - new InlineMethod(Static, "Ljava/lang/Math;", "abs", "D", "D"), - new InlineMethod(Static, "Ljava/lang/Math;", "min", "II", "I"), - new InlineMethod(Static, "Ljava/lang/Math;", "max", "II", "I"), - new InlineMethod(Static, "Ljava/lang/Math;", "sqrt", "D", "D"), - new InlineMethod(Static, "Ljava/lang/Math;", "cos", "D", "D"), - new InlineMethod(Static, "Ljava/lang/Math;", "sin", "D", "D") - }; + private final InlineMethodResolver inlineMethodResolver; public final DexFile dexFile; @@ -86,21 +50,12 @@ public class DeodexUtil { //if there isn't an odex header, why are we creating an DeodexUtil object? assert false; throw new RuntimeException("Cannot create a DeodexUtil object for a dex file without an odex header"); - } else if (odexHeader.version == 35) { - inlineMethods = inlineMethods_35; - } else if (odexHeader.version == 36) { - inlineMethods = inlineMethods_36; - } else { - assert false; - throw new RuntimeException(String.format("odex version %d isn't supported yet", odexHeader.version)); } + inlineMethodResolver = InlineMethodResolver.createInlineMethodResolver(this, odexHeader.version); } - public InlineMethod lookupInlineMethod(int inlineMethodIndex) { - if (inlineMethodIndex >= inlineMethods.length) { - throw new RuntimeException("Invalid inline method index " + inlineMethodIndex + "."); - } - return inlineMethods[inlineMethodIndex]; + public InlineMethod lookupInlineMethod(AnalyzedInstruction instruction) { + return inlineMethodResolver.resolveExecuteInline(instruction); } public FieldIdItem lookupField(ClassPath.ClassDef classDef, int fieldOffset) { @@ -297,7 +252,7 @@ public class DeodexUtil { private MethodIdItem methodIdItem = null; - protected InlineMethod(int methodType, String classType, String methodName, String parameters, + InlineMethod(int methodType, String classType, String methodName, String parameters, String returnType) { this.methodType = methodType; this.classType = classType; diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/InlineMethodResolver.java b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/InlineMethodResolver.java new file mode 100644 index 00000000..e86e9726 --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/InlineMethodResolver.java @@ -0,0 +1,186 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib.Code.Analysis; + +import org.jf.dexlib.Code.Format.Instruction35ms; +import org.jf.dexlib.Code.Format.Instruction3rms; +import org.jf.dexlib.Code.OdexedInvokeVirtual; +import org.jf.dexlib.MethodIdItem; + +import static org.jf.dexlib.Code.Analysis.DeodexUtil.Static; +import static org.jf.dexlib.Code.Analysis.DeodexUtil.Virtual; +import static org.jf.dexlib.Code.Analysis.DeodexUtil.Direct; + +abstract class InlineMethodResolver { + public static InlineMethodResolver createInlineMethodResolver(DeodexUtil deodexUtil, int odexVersion) { + if (odexVersion == 35) { + return new InlineMethodResolver_version35(deodexUtil); + } else if (odexVersion == 36) { + return new InlineMethodResolver_version36(deodexUtil); + } else { + throw new RuntimeException(String.format("odex version %d is not supported yet", odexVersion)); + } + } + + private InlineMethodResolver() { + } + + public abstract DeodexUtil.InlineMethod resolveExecuteInline(AnalyzedInstruction instruction); + + private static class InlineMethodResolver_version35 extends InlineMethodResolver + { + private final DeodexUtil.InlineMethod[] inlineMethods; + + public InlineMethodResolver_version35(DeodexUtil deodexUtil) { + inlineMethods = new DeodexUtil.InlineMethod[] { + deodexUtil.new InlineMethod(Static, "Lorg/apache/harmony/dalvik/NativeTestTarget;", "emptyInlineMethod", "", "V"), + deodexUtil.new InlineMethod(Virtual, "Ljava/lang/String;", "charAt", "I", "C"), + deodexUtil.new InlineMethod(Virtual, "Ljava/lang/String;", "compareTo", "Ljava/lang/String;", "I"), + deodexUtil.new InlineMethod(Virtual, "Ljava/lang/String;", "equals", "Ljava/lang/Object;", "Z"), + deodexUtil.new InlineMethod(Virtual, "Ljava/lang/String;", "length", "", "I"), + deodexUtil.new InlineMethod(Static, "Ljava/lang/Math;", "abs", "I", "I"), + deodexUtil.new InlineMethod(Static, "Ljava/lang/Math;", "abs", "J", "J"), + deodexUtil.new InlineMethod(Static, "Ljava/lang/Math;", "abs", "F", "F"), + deodexUtil.new InlineMethod(Static, "Ljava/lang/Math;", "abs", "D", "D"), + deodexUtil.new InlineMethod(Static, "Ljava/lang/Math;", "min", "II", "I"), + deodexUtil.new InlineMethod(Static, "Ljava/lang/Math;", "max", "II", "I"), + deodexUtil.new InlineMethod(Static, "Ljava/lang/Math;", "sqrt", "D", "D"), + deodexUtil.new InlineMethod(Static, "Ljava/lang/Math;", "cos", "D", "D"), + deodexUtil.new InlineMethod(Static, "Ljava/lang/Math;", "sin", "D", "D") + }; + } + + @Override + public DeodexUtil.InlineMethod resolveExecuteInline(AnalyzedInstruction analyzedInstruction) { + assert analyzedInstruction.instruction instanceof OdexedInvokeVirtual; + + OdexedInvokeVirtual instruction = (OdexedInvokeVirtual)analyzedInstruction; + int methodIndex = instruction.getMethodIndex(); + + if (methodIndex < 0 || methodIndex >= inlineMethods.length) { + throw new RuntimeException("Invalid method index: " + methodIndex); + } + return inlineMethods[methodIndex]; + } + }; + + private static class InlineMethodResolver_version36 extends InlineMethodResolver + { + private final DeodexUtil.InlineMethod[] inlineMethods; + private final DeodexUtil.InlineMethod indexOfIMethod; + private final DeodexUtil.InlineMethod indexOfIIMethod; + private final DeodexUtil.InlineMethod fastIndexOfMethod; + private final DeodexUtil.InlineMethod isEmptyMethod; + + + public InlineMethodResolver_version36(DeodexUtil deodexUtil) { + //The 5th and 6th entries differ between froyo and gingerbread. We have to look at the parameters being + //passed to distinguish between them. + + //froyo + indexOfIMethod = deodexUtil.new InlineMethod(Virtual, "Ljava/lang/String;", "indexOf", "I", "I"); + indexOfIIMethod = deodexUtil.new InlineMethod(Virtual, "Ljava/lang/String;", "indexOf", "II", "I"); + + //gingerbread + fastIndexOfMethod = deodexUtil.new InlineMethod(Virtual, "Ljava/lang/String;", "fastIndexOf", "II", "I"); + isEmptyMethod = deodexUtil.new InlineMethod(Virtual, "Ljava/lang/String;", "isEmpty", "", "Z"); + + inlineMethods = new DeodexUtil.InlineMethod[] { + deodexUtil.new InlineMethod(Static, "Lorg/apache/harmony/dalvik/NativeTestTarget;", "emptyInlineMethod", "", "V"), + deodexUtil.new InlineMethod(Virtual, "Ljava/lang/String;", "charAt", "I", "C"), + deodexUtil.new InlineMethod(Virtual, "Ljava/lang/String;", "compareTo", "Ljava/lang/String;", "I"), + deodexUtil.new InlineMethod(Virtual, "Ljava/lang/String;", "equals", "Ljava/lang/Object;", "Z"), + //froyo: deodexUtil.new InlineMethod(Virtual, "Ljava/lang/String;", "indexOf", "I", "I"), + //gingerbread: deodexUtil.new InlineMethod(Virtual, "Ljava/lang/String;", "fastIndexOf", "II", "I"), + null, + //froyo: deodexUtil.new InlineMethod(Virtual, "Ljava/lang/String;", "indexOf", "II", "I"), + //gingerbread: deodexUtil.new InlineMethod(Virtual, "Ljava/lang/String;", "isEmpty", "", "Z"), + null, + deodexUtil.new InlineMethod(Virtual, "Ljava/lang/String;", "length", "", "I"), + deodexUtil.new InlineMethod(Static, "Ljava/lang/Math;", "abs", "I", "I"), + deodexUtil.new InlineMethod(Static, "Ljava/lang/Math;", "abs", "J", "J"), + deodexUtil.new InlineMethod(Static, "Ljava/lang/Math;", "abs", "F", "F"), + deodexUtil.new InlineMethod(Static, "Ljava/lang/Math;", "abs", "D", "D"), + deodexUtil.new InlineMethod(Static, "Ljava/lang/Math;", "min", "II", "I"), + deodexUtil.new InlineMethod(Static, "Ljava/lang/Math;", "max", "II", "I"), + deodexUtil.new InlineMethod(Static, "Ljava/lang/Math;", "sqrt", "D", "D"), + deodexUtil.new InlineMethod(Static, "Ljava/lang/Math;", "cos", "D", "D"), + deodexUtil.new InlineMethod(Static, "Ljava/lang/Math;", "sin", "D", "D"), + deodexUtil.new InlineMethod(Static, "Ljava/lang/Float;", "floatToIntBits", "F", "I"), + deodexUtil.new InlineMethod(Static, "Ljava/lang/Float;", "floatToRawIntBits", "F", "I"), + deodexUtil.new InlineMethod(Static, "Ljava/lang/Float;", "intBitsToFloat", "I", "F"), + deodexUtil.new InlineMethod(Static, "Ljava/lang/Double;", "doubleToLongBits", "D", "J"), + deodexUtil.new InlineMethod(Static, "Ljava/lang/Double;", "doubleToRawLongBits", "D", "J"), + deodexUtil.new InlineMethod(Static, "Ljava/lang/Double;", "longBitsToDouble", "J", "D") + }; + } + + @Override + public DeodexUtil.InlineMethod resolveExecuteInline(AnalyzedInstruction analyzedInstruction) { + assert analyzedInstruction.instruction instanceof OdexedInvokeVirtual; + + OdexedInvokeVirtual instruction = (OdexedInvokeVirtual)analyzedInstruction.instruction; + int methodIndex = instruction.getMethodIndex(); + + if (methodIndex < 0 || methodIndex >= inlineMethods.length) { + throw new RuntimeException("Invalid method index: " + methodIndex); + } + + if (methodIndex == 4) { + int parameterCount = getParameterCount(instruction); + if (parameterCount == 2) { + return indexOfIMethod; + } else if (parameterCount == 3) { + return fastIndexOfMethod; + } else { + throw new RuntimeException("Could not determine the correct inline method to use"); + } + } else if (methodIndex == 5) { + int parameterCount = getParameterCount(instruction); + if (parameterCount == 3) { + return indexOfIIMethod; + } else if (parameterCount == 1) { + return isEmptyMethod; + } else { + throw new RuntimeException("Could not determine the correct inline method to use"); + } + } + + return inlineMethods[methodIndex]; + } + + private int getParameterCount(OdexedInvokeVirtual instruction) { + if (instruction instanceof Instruction35ms) { + return ((Instruction35ms)instruction).getRegCount(); + } else { + return ((Instruction3rms)instruction).getRegCount(); + } + } + }; +} 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 0d5b6a17..f4d7c49e 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 @@ -3308,11 +3308,11 @@ public class MethodAnalyzer { Instruction35ms instruction = (Instruction35ms)analyzedInstruction.instruction; - int methodIndex = instruction.getMethodIndex(); - DeodexUtil.InlineMethod inlineMethod = deodexUtil.lookupInlineMethod(methodIndex); + DeodexUtil.InlineMethod inlineMethod = deodexUtil.lookupInlineMethod(analyzedInstruction); MethodIdItem inlineMethodIdItem = inlineMethod.getMethodIdItem(); if (inlineMethodIdItem == null) { - throw new ValidationException(String.format("Cannot load inline method with index %d", methodIndex)); + throw new ValidationException(String.format("Cannot load inline method with index %d", + instruction.getMethodIndex())); } Opcode deodexedOpcode = null; @@ -3346,11 +3346,11 @@ public class MethodAnalyzer { Instruction3rms instruction = (Instruction3rms)analyzedInstruction.instruction; - int methodIndex = instruction.getMethodIndex(); - DeodexUtil.InlineMethod inlineMethod = deodexUtil.lookupInlineMethod(methodIndex); + DeodexUtil.InlineMethod inlineMethod = deodexUtil.lookupInlineMethod(analyzedInstruction); MethodIdItem inlineMethodIdItem = inlineMethod.getMethodIdItem(); if (inlineMethodIdItem == null) { - throw new ValidationException(String.format("Cannot load inline method with index %d", methodIndex)); + throw new ValidationException(String.format("Cannot load inline method with index %d", + instruction.getMethodIndex())); } Opcode deodexedOpcode = null;