From 7025dfb7535319b39a806ae9124a939d29982cb0 Mon Sep 17 00:00:00 2001 From: "JesusFreke@JesusFreke.com" Date: Wed, 3 Mar 2010 03:26:33 +0000 Subject: [PATCH] Split the analysis logic into an analysis pass and a verification pass git-svn-id: https://smali.googlecode.com/svn/trunk@663 55b6fa8a-2a1e-11de-a435-ffa8d773f76a --- .../dexlib/Code/Analysis/MethodAnalyzer.java | 1605 +++++++++++------ 1 file changed, 1100 insertions(+), 505 deletions(-) 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 52762f1a..0b28dde0 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 @@ -36,6 +36,16 @@ import org.jf.dexlib.Util.*; import java.util.*; +/** + * The MethodAnalyzer performs several functions. It "analyzes" the instructions and infers the register types + * for each register, it can deodex odexed instructions, and it can verify the bytecode. The analysis and verification + * are done in two separate passes, because the analysis has to process instructions multiple times in some cases, and + * there's no need to perform the verification multiple times, so we wait until the method is fully analyzed and then + * verify it. + * + * Before calling the analyze() method, you must have initialized the ClassPath by calling + * ClassPath.InitializeClassPath + */ public class MethodAnalyzer { private final ClassDataItem.EncodedMethod encodedMethod; @@ -48,7 +58,7 @@ public class MethodAnalyzer { private static final int VERIFIED = 2; private int analyzerState = NOT_ANALYZED; - private BitSet verifiedInstructions; + private BitSet analyzedInstructions; private ValidationException validationException = null; @@ -99,7 +109,7 @@ public class MethodAnalyzer { buildInstructionList(); - verifiedInstructions = new BitSet(instructions.size()); + analyzedInstructions = new BitSet(instructions.size()); } public boolean isAnalyzed() { @@ -135,19 +145,10 @@ public class MethodAnalyzer { //if this is a constructor, then set the "this" register to an uninitialized reference of the current class if ((encodedMethod.accessFlags & AccessFlags.CONSTRUCTOR.getValue()) != 0) { - //TODO: it would probably make more sense to validate this somewhere else, and just put an assert here. Also, need to do a similar check for static constructor - if (!encodedMethod.method.getMethodName().getStringValue().equals("")) { - throw new ValidationException("The constructor flag can only be used with an method."); - } - setPostRegisterTypeAndPropagateChanges(startOfMethod, thisRegister, RegisterType.getRegisterType(RegisterType.Category.UninitThis, ClassPath.getClassDef(methodIdItem.getContainingClass()))); } else { - if (encodedMethod.method.getMethodName().getStringValue().equals("")) { - throw new ValidationException("An method must have the \"constructor\" access flag"); - } - setPostRegisterTypeAndPropagateChanges(startOfMethod, thisRegister, RegisterType.getRegisterType(RegisterType.Category.Reference, ClassPath.getClassDef(methodIdItem.getContainingClass()))); @@ -169,14 +170,14 @@ public class MethodAnalyzer { setPostRegisterTypeAndPropagateChanges(startOfMethod, i, uninit); } - BitSet instructionsToAnalyze = new BitSet(verifiedInstructions.size()); + BitSet instructionsToAnalyze = new BitSet(instructions.size()); //make sure all of the "first instructions" are marked for processing for (AnalyzedInstruction successor: startOfMethod.successors) { instructionsToAnalyze.set(successor.instructionIndex); } - BitSet odexedInstructions = new BitSet(verifiedInstructions.size()); + BitSet odexedInstructions = new BitSet(instructions.size()); do { boolean didSomething = false; @@ -184,16 +185,16 @@ public class MethodAnalyzer { while (!instructionsToAnalyze.isEmpty()) { for(int i=instructionsToAnalyze.nextSetBit(0); i>=0; i=instructionsToAnalyze.nextSetBit(i+1)) { instructionsToAnalyze.clear(i); - if (verifiedInstructions.get(i)) { + if (analyzedInstructions.get(i)) { continue; } - AnalyzedInstruction instructionToVerify = instructions.valueAt(i); + AnalyzedInstruction instructionToAnalyze = instructions.valueAt(i); try { - if (instructionToVerify.originalInstruction.opcode.odexOnly()) { - instructionToVerify.restoreOdexedInstruction(); + if (instructionToAnalyze.originalInstruction.opcode.odexOnly()) { + instructionToAnalyze.restoreOdexedInstruction(); } - if (!analyzeInstruction(instructionToVerify)) { + if (!analyzeInstruction(instructionToAnalyze)) { odexedInstructions.set(i); continue; } else { @@ -202,17 +203,17 @@ public class MethodAnalyzer { } } catch (ValidationException ex) { this.validationException = ex; - int codeAddress = getInstructionAddress(instructionToVerify); + int codeAddress = getInstructionAddress(instructionToAnalyze); ex.setCodeAddress(codeAddress); - ex.addContext(String.format("opcode: %s", instructionToVerify.instruction.opcode.name)); + ex.addContext(String.format("opcode: %s", instructionToAnalyze.instruction.opcode.name)); ex.addContext(String.format("CodeAddress: %d", codeAddress)); ex.addContext(String.format("Method: %s", encodedMethod.method.getMethodString())); break; } - verifiedInstructions.set(instructionToVerify.getInstructionIndex()); + analyzedInstructions.set(instructionToAnalyze.getInstructionIndex()); - for (AnalyzedInstruction successor: instructionToVerify.successors) { + for (AnalyzedInstruction successor: instructionToAnalyze.successors) { instructionsToAnalyze.set(successor.getInstructionIndex()); } } @@ -232,48 +233,58 @@ public class MethodAnalyzer { } } while (true); - for (int i=0; i=0; i=instructionsToVerify.nextSetBit(i+1)) { + instructionsToVerify.clear(i); if (verifiedInstructions.get(i)) { - for (int j=0; j Primitive32BitCategories = EnumSet.of( RegisterType.Category.Null, RegisterType.Category.One, @@ -1186,20 +1559,43 @@ public class MethodAnalyzer { RegisterType.Category.One, RegisterType.Category.Boolean); - private void handleMove(AnalyzedInstruction analyzedInstruction, EnumSet validCategories) { + private void analyzeMove(AnalyzedInstruction analyzedInstruction) { TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; - RegisterType sourceRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), - validCategories); - + RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, sourceRegisterType); } - private void handleMoveResult(AnalyzedInstruction analyzedInstruction, + private void verifyMove(AnalyzedInstruction analyzedInstruction, EnumSet validCategories) { + TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; + + getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), validCategories); + } + + private void analyzeMoveResult(AnalyzedInstruction analyzedInstruction) { + AnalyzedInstruction previousInstruction = instructions.valueAt(analyzedInstruction.instructionIndex-1); + if (!previousInstruction.instruction.opcode.setsResult()) { + throw new ValidationException(analyzedInstruction.instruction.opcode.name + " must occur after an " + + "invoke-*/fill-new-array instruction"); + } + + RegisterType resultRegisterType; + InstructionWithReference invokeInstruction = (InstructionWithReference)previousInstruction.instruction; + Item item = invokeInstruction.getReferencedItem(); + + if (item.getItemType() == ItemType.TYPE_METHOD_ID_ITEM) { + resultRegisterType = RegisterType.getRegisterTypeForTypeIdItem( + ((MethodIdItem)item).getPrototype().getReturnType()); + } else { + assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM; + resultRegisterType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item); + } + + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, resultRegisterType); + } + + private void verifyMoveResult(AnalyzedInstruction analyzedInstruction, EnumSet allowedCategories) { - - //TODO: handle the case when the previous instruction is an odexed instruction - if (analyzedInstruction.instructionIndex == 0) { throw new ValidationException(analyzedInstruction.instruction.opcode.name + " cannot be the first " + "instruction in a method. It must occur after an invoke-*/fill-new-array instruction"); @@ -1215,7 +1611,7 @@ public class MethodAnalyzer { //TODO: does dalvik allow a move-result after an invoke with a void return type? RegisterType resultRegisterType; - InstructionWithReference invokeInstruction = (InstructionWithReference)previousInstruction.instruction; + InstructionWithReference invokeInstruction = (InstructionWithReference)previousInstruction.getInstruction(); Item item = invokeInstruction.getReferencedItem(); if (item instanceof MethodIdItem) { @@ -1230,11 +1626,40 @@ public class MethodAnalyzer { throw new ValidationException(String.format("Wrong move-result* instruction for return value %s", resultRegisterType.toString())); } - - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, resultRegisterType); } - private void handleMoveException(AnalyzedInstruction analyzedInstruction) { + private void analyzeMoveException(AnalyzedInstruction analyzedInstruction) { + CodeItem.TryItem[] tries = encodedMethod.codeItem.getTries(); + int instructionAddress = getInstructionAddress(analyzedInstruction); + + if (tries == null) { + throw new ValidationException("move-exception must be the first instruction in an exception handler block"); + } + + RegisterType exceptionType = null; + + for (CodeItem.TryItem tryItem: encodedMethod.codeItem.getTries()) { + if (tryItem.encodedCatchHandler.getCatchAllHandlerAddress() == instructionAddress) { + exceptionType = RegisterType.getRegisterType(RegisterType.Category.Reference, + ClassPath.getClassDef("Ljava/lang/Throwable;")); + break; + } + for (CodeItem.EncodedTypeAddrPair handler: tryItem.encodedCatchHandler.handlers) { + if (handler.getHandlerAddress() == instructionAddress) { + exceptionType = RegisterType.getRegisterTypeForTypeIdItem(handler.exceptionType) + .merge(exceptionType); + } + } + } + + if (exceptionType == null) { + throw new ValidationException("move-exception must be the first instruction in an exception handler block"); + } + + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, exceptionType); + } + + private void verifyMoveException(AnalyzedInstruction analyzedInstruction) { CodeItem.TryItem[] tries = encodedMethod.codeItem.getTries(); int instructionAddress = getInstructionAddress(analyzedInstruction); @@ -1267,11 +1692,9 @@ public class MethodAnalyzer { throw new ValidationException(String.format("Exception type %s is not a reference type", exceptionType.toString())); } - - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, exceptionType); } - private void handleReturnVoid(AnalyzedInstruction analyzedInstruction) { + private void verifyReturnVoid(AnalyzedInstruction analyzedInstruction) { TypeIdItem returnType = encodedMethod.method.getPrototype().getReturnType(); if (returnType.getTypeDescriptor().charAt(0) != 'V') { //TODO: could add which return-* variation should be used instead @@ -1280,7 +1703,7 @@ public class MethodAnalyzer { } } - private void handleReturn(AnalyzedInstruction analyzedInstruction, EnumSet validCategories) { + private void verifyReturn(AnalyzedInstruction analyzedInstruction, EnumSet validCategories) { /*if (this.isInstanceConstructor()) { checkConstructorReturn(analyzedInstruction); }*/ @@ -1321,7 +1744,7 @@ public class MethodAnalyzer { } } - private void handleConst(AnalyzedInstruction analyzedInstruction) { + private void analyzeConst(AnalyzedInstruction analyzedInstruction) { LiteralInstruction instruction = (LiteralInstruction)analyzedInstruction.instruction; RegisterType newDestinationRegisterType = RegisterType.getRegisterTypeForLiteral(instruction.getLiteral()); @@ -1331,29 +1754,33 @@ public class MethodAnalyzer { setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, newDestinationRegisterType); } - private void handleConstHigh16(AnalyzedInstruction analyzedInstruction) { - LiteralInstruction instruction = (LiteralInstruction)analyzedInstruction.instruction; - - long literalValue = instruction.getLiteral() << 16; - RegisterType newDestinationRegisterType = RegisterType.getRegisterTypeForLiteral(literalValue); - - //we assume that the literal value is a valid value for the given instruction type, because it's impossible - //to store an invalid literal with the instruction. so we don't need to check the type of the literal - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, newDestinationRegisterType); + private void analyzeConstHigh16(AnalyzedInstruction analyzedInstruction) { + //the literal value stored in the instruction is a 16-bit value. When shifted left by 16, it will always be an + //integer + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, + RegisterType.getRegisterType(RegisterType.Category.Integer, null)); } - private void handleWideConst(AnalyzedInstruction analyzedInstruction) { + private void analyzeWideConst(AnalyzedInstruction analyzedInstruction) { setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, RegisterType.getRegisterType(RegisterType.Category.LongLo, null)); } - private void handleConstString(AnalyzedInstruction analyzedInstruction) { + private void analyzeConstString(AnalyzedInstruction analyzedInstruction) { ClassPath.ClassDef stringClassDef = ClassPath.getClassDef("Ljava/lang/String;"); RegisterType stringType = RegisterType.getRegisterType(RegisterType.Category.Reference, stringClassDef); setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, stringType); } - private void handleConstClass(AnalyzedInstruction analyzedInstruction) { + private void analyzeConstClass(AnalyzedInstruction analyzedInstruction) { + ClassPath.ClassDef classClassDef = ClassPath.getClassDef("Ljava/lang/Class;"); + RegisterType classType = RegisterType.getRegisterType(RegisterType.Category.Reference, classClassDef); + + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, classType); + } + + + private void verifyConstClass(AnalyzedInstruction analyzedInstruction) { ClassPath.ClassDef classClassDef = ClassPath.getClassDef("Ljava/lang/Class;"); RegisterType classType = RegisterType.getRegisterType(RegisterType.Category.Reference, classClassDef); @@ -1364,16 +1791,24 @@ public class MethodAnalyzer { //TODO: need to check class access //make sure the referenced class is resolvable ClassPath.getClassDef((TypeIdItem)item); - - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, classType); } - private void handleMonitor(AnalyzedInstruction analyzedInstruction) { + private void verifyMonitor(AnalyzedInstruction analyzedInstruction) { SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), ReferenceCategories); } - private void handleCheckCast(AnalyzedInstruction analyzedInstruction) { + private void analyzeCheckCast(AnalyzedInstruction analyzedInstruction) { + InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; + + Item item = instruction.getReferencedItem(); + assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM; + + RegisterType castRegisterType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item); + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, castRegisterType); + } + + private void verifyCheckCast(AnalyzedInstruction analyzedInstruction) { { //ensure the "source" register is a reference type SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; @@ -1395,12 +1830,15 @@ public class MethodAnalyzer { //TODO: verify that dalvik allows a non-reference type.. //TODO: print a warning, but don't re-throw the exception. dalvik allows a non-reference type during validation (but throws an exception at runtime) } - - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, castRegisterType); } } - private void handleInstanceOf(AnalyzedInstruction analyzedInstruction) { + private void analyzeInstanceOf(AnalyzedInstruction analyzedInstruction) { + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, + RegisterType.getRegisterType(RegisterType.Category.Boolean, null)); + } + + private void verifyInstanceOf(AnalyzedInstruction analyzedInstruction) { { //ensure the register that is being checks is a reference type TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; @@ -1421,14 +1859,16 @@ public class MethodAnalyzer { } //TODO: is it valid to use an array type? - //TODO: could probably do an even more sophisticated check, where we check the possible register types against the specified type. In some cases, we could determine that it always fails, and print a warning to that effect. - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, - RegisterType.getRegisterType(RegisterType.Category.Boolean, null)); } } - private void handleArrayLength(AnalyzedInstruction analyzedInstruction) { + private void analyzeArrayLength(AnalyzedInstruction analyzedInstruction) { + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, + RegisterType.getRegisterType(RegisterType.Category.Integer, null)); + } + + private void verifyArrayLength(AnalyzedInstruction analyzedInstruction) { TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; int arrayRegisterNumber = instruction.getRegisterB(); @@ -1442,12 +1882,32 @@ public class MethodAnalyzer { } assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef; } - - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, - RegisterType.getRegisterType(RegisterType.Category.Integer, null)); } - private void handleNewInstance(AnalyzedInstruction analyzedInstruction) { + private void analyzeNewInstance(AnalyzedInstruction analyzedInstruction) { + InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; + + int register = ((SingleRegisterInstruction)analyzedInstruction.instruction).getRegisterA(); + RegisterType destRegisterType = analyzedInstruction.getPostInstructionRegisterType(register); + if (destRegisterType.category != RegisterType.Category.Unknown) { + assert destRegisterType.category == RegisterType.Category.UninitRef; + + //the post-instruction destination register will only be set if we have already analyzed this instruction + //at least once. If this is the case, then the uninit reference has already been propagated to all + //successors and nothing else needs to be done. + return; + } + + Item item = instruction.getReferencedItem(); + assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM; + + RegisterType classType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item); + + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, + RegisterType.getUnitializedReference(classType.type)); + } + + private void verifyNewInstance(AnalyzedInstruction analyzedInstruction) { InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; int register = ((SingleRegisterInstruction)analyzedInstruction.instruction).getRegisterA(); @@ -1488,12 +1948,21 @@ public class MethodAnalyzer { throw new ValidationException("Cannot use array type \"" + ((TypeIdItem)item).getTypeDescriptor() + "\" with new-instance. Use new-array instead."); } - - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, - RegisterType.getUnitializedReference(classType.type)); } - private void handleNewArray(AnalyzedInstruction analyzedInstruction) { + private void analyzeNewArray(AnalyzedInstruction analyzedInstruction) { + InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; + + Item item = instruction.getReferencedItem(); + assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM; + + RegisterType arrayType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item); + assert arrayType.type instanceof ClassPath.ArrayClassDef; + + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, arrayType); + } + + private void verifyNewArray(AnalyzedInstruction analyzedInstruction) { { TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), Primitive32BitCategories); @@ -1515,76 +1984,9 @@ public class MethodAnalyzer { throw new ValidationException("Cannot use non-array type \"" + arrayType.type.getClassType() + "\" with new-array. Use new-instance instead."); } - - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, arrayType); } - private static interface RegisterIterator { - int getRegister(); - boolean moveNext(); - int getCount(); - boolean pastEnd(); - } - - private static class Format35cRegisterIterator implements RegisterIterator { - private final int registerCount; - private final int[] registers; - private int currentRegister = 0; - - public Format35cRegisterIterator(FiveRegisterInstruction instruction) { - registerCount = instruction.getRegCount(); - registers = new int[]{instruction.getRegisterD(), instruction.getRegisterE(), - instruction.getRegisterF(), instruction.getRegisterG(), - instruction.getRegisterA()}; - } - - public int getRegister() { - return registers[currentRegister]; - } - - public boolean moveNext() { - currentRegister++; - return !pastEnd(); - } - - public int getCount() { - return registerCount; - } - - public boolean pastEnd() { - return currentRegister >= registerCount; - } - } - - private static class Format3rcRegisterIterator implements RegisterIterator { - private final int startRegister; - private final int registerCount; - private int currentRegister = 0; - - public Format3rcRegisterIterator(RegisterRangeInstruction instruction) { - startRegister = instruction.getStartRegister(); - registerCount = instruction.getRegCount(); - } - - public int getRegister() { - return startRegister + currentRegister; - } - - public boolean moveNext() { - currentRegister++; - return !pastEnd(); - } - - public int getCount() { - return registerCount; - } - - public boolean pastEnd() { - return currentRegister >= registerCount; - } - } - - private void handleFilledNewArrayCommon(AnalyzedInstruction analyzedInstruction, + private void verifyFilledNewArrayCommon(AnalyzedInstruction analyzedInstruction, RegisterIterator registerIterator) { InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; @@ -1624,12 +2026,12 @@ public class MethodAnalyzer { } while (registerIterator.moveNext()); } - private void handleFilledNewArray(AnalyzedInstruction analyzedInstruction) { + private void verifyFilledNewArray(AnalyzedInstruction analyzedInstruction) { FiveRegisterInstruction instruction = (FiveRegisterInstruction)analyzedInstruction.instruction; - handleFilledNewArrayCommon(analyzedInstruction, new Format35cRegisterIterator(instruction)); + verifyFilledNewArrayCommon(analyzedInstruction, new Format35cRegisterIterator(instruction)); } - private void handleFilledNewArrayRange(AnalyzedInstruction analyzedInstruction) { + private void verifyFilledNewArrayRange(AnalyzedInstruction analyzedInstruction) { RegisterRangeInstruction instruction = (RegisterRangeInstruction)analyzedInstruction.instruction; //instruction.getStartRegister() and instruction.getRegCount() both return an int value, but are actually @@ -1641,10 +2043,10 @@ public class MethodAnalyzer { instruction.getStartRegister() + instruction.getRegCount() - 1)); } - handleFilledNewArrayCommon(analyzedInstruction, new Format3rcRegisterIterator(instruction)); + verifyFilledNewArrayCommon(analyzedInstruction, new Format3rcRegisterIterator(instruction)); } - private void handleFillArrayData(AnalyzedInstruction analyzedInstruction) { + private void verifyFillArrayData(AnalyzedInstruction analyzedInstruction) { SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; int register = instruction.getRegisterA(); @@ -1711,7 +2113,7 @@ public class MethodAnalyzer { } } - private void handleThrow(AnalyzedInstruction analyzedInstruction) { + private void verifyThrow(AnalyzedInstruction analyzedInstruction) { int register = ((SingleRegisterInstruction)analyzedInstruction.instruction).getRegisterA(); RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(register); @@ -1734,7 +2136,7 @@ public class MethodAnalyzer { } } - private void handleSwitch(AnalyzedInstruction analyzedInstruction, Format expectedSwitchDataFormat) { + private void verifySwitch(AnalyzedInstruction analyzedInstruction, Format expectedSwitchDataFormat) { int register = ((SingleRegisterInstruction)analyzedInstruction.instruction).getRegisterA(); int switchCodeAddressOffset = ((OffsetInstruction)analyzedInstruction.instruction).getTargetAddressOffset(); @@ -1750,17 +2152,19 @@ public class MethodAnalyzer { } } - private void handleFloatWideCmp(AnalyzedInstruction analyzedInstruction, EnumSet validCategories) { - ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; - - getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), validCategories); - getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), validCategories); - + private void analyzeFloatWideCmp(AnalyzedInstruction analyzedInstruction) { setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, RegisterType.getRegisterType(RegisterType.Category.Byte, null)); } - private void handleIfEqNe(AnalyzedInstruction analyzedInstruction) { + private void verifyFloatWideCmp(AnalyzedInstruction analyzedInstruction, EnumSet validCategories) { + ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; + + getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), validCategories); + getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), validCategories); + } + + private void verifyIfEqNe(AnalyzedInstruction analyzedInstruction) { TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; RegisterType registerType1 = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA()); @@ -1783,27 +2187,33 @@ public class MethodAnalyzer { } } - private void handleIf(AnalyzedInstruction analyzedInstruction) { + private void verifyIf(AnalyzedInstruction analyzedInstruction) { TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), Primitive32BitCategories); getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), Primitive32BitCategories); } - private void handleIfEqzNez(AnalyzedInstruction analyzedInstruction) { + private void verifyIfEqzNez(AnalyzedInstruction analyzedInstruction) { SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), ReferenceAndPrimitive32BitCategories); } - private void handleIfz(AnalyzedInstruction analyzedInstruction) { + private void verifyIfz(AnalyzedInstruction analyzedInstruction) { SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), Primitive32BitCategories); } - private void handle32BitPrimitiveAget(AnalyzedInstruction analyzedInstruction, + private void analyze32BitPrimitiveAget(AnalyzedInstruction analyzedInstruction, + RegisterType.Category instructionCategory) { + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, + RegisterType.getRegisterType(instructionCategory, null)); + } + + private void verify32BitPrimitiveAget(AnalyzedInstruction analyzedInstruction, RegisterType.Category instructionCategory) { ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; @@ -1840,12 +2250,42 @@ public class MethodAnalyzer { arrayRegisterType.type.getClassType())); } } - - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, - RegisterType.getRegisterType(instructionCategory, null)); } - private void handleAgetWide(AnalyzedInstruction analyzedInstruction) { + private void analyzeAgetWide(AnalyzedInstruction analyzedInstruction) { + ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; + + RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); + assert arrayRegisterType != null; + + if (arrayRegisterType.category != RegisterType.Category.Null) { + assert arrayRegisterType.type != null; + if (arrayRegisterType.type.getClassType().charAt(0) != '[') { + throw new ValidationException(String.format("Cannot use aget-wide with non-array type %s", + arrayRegisterType.type.getClassType())); + } + + assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef; + ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type; + + char arrayBaseType = arrayClassDef.getBaseElementClass().getClassType().charAt(0); + if (arrayBaseType == 'J') { + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, + RegisterType.getRegisterType(RegisterType.Category.LongLo, null)); + } else if (arrayBaseType == 'D') { + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, + RegisterType.getRegisterType(RegisterType.Category.DoubleLo, null)); + } else { + throw new ValidationException(String.format("Cannot use aget-wide with array type %s. Incorrect " + + "array type for the instruction.", arrayRegisterType.type.getClassType())); + } + } else { + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, + RegisterType.getRegisterType(RegisterType.Category.LongLo, null)); + } + } + + private void verifyAgetWide(AnalyzedInstruction analyzedInstruction) { ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), Primitive32BitCategories); @@ -1874,23 +2314,45 @@ public class MethodAnalyzer { } char arrayBaseType = arrayClassDef.getBaseElementClass().getClassType().charAt(0); - if (arrayBaseType == 'J') { - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, - RegisterType.getRegisterType(RegisterType.Category.LongLo, null)); - } else if (arrayBaseType == 'D') { - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, - RegisterType.getRegisterType(RegisterType.Category.DoubleLo, null)); - } else { + if (arrayBaseType != 'J' && arrayBaseType != 'D') { throw new ValidationException(String.format("Cannot use aget-wide with array type %s. Incorrect " + "array type for the instruction.", arrayRegisterType.type.getClassType())); } - } else { - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, - RegisterType.getRegisterType(RegisterType.Category.LongLo, null)); } } - private void handleAgetObject(AnalyzedInstruction analyzedInstruction) { + private void analyzeAgetObject(AnalyzedInstruction analyzedInstruction) { + ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; + + RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); + assert arrayRegisterType != null; + + if (arrayRegisterType.category != RegisterType.Category.Null) { + assert arrayRegisterType.type != null; + if (arrayRegisterType.type.getClassType().charAt(0) != '[') { + throw new ValidationException(String.format("Cannot use aget-object with non-array type %s", + arrayRegisterType.type.getClassType())); + } + + assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef; + ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type; + + ClassPath.ClassDef elementClassDef = arrayClassDef.getImmediateElementClass(); + char elementTypePrefix = elementClassDef.getClassType().charAt(0); + if (elementTypePrefix != 'L' && elementTypePrefix != '[') { + throw new ValidationException(String.format("Cannot use aget-object with array type %s. Incorrect " + + "array type for the instruction.", arrayRegisterType.type.getClassType())); + } + + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, + RegisterType.getRegisterType(RegisterType.Category.Reference, elementClassDef)); + } else { + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, + RegisterType.getRegisterType(RegisterType.Category.Null, null)); + } + } + + private void verifyAgetObject(AnalyzedInstruction analyzedInstruction) { ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), Primitive32BitCategories); @@ -1919,16 +2381,10 @@ public class MethodAnalyzer { throw new ValidationException(String.format("Cannot use aget-object with array type %s. Incorrect " + "array type for the instruction.", arrayRegisterType.type.getClassType())); } - - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, - RegisterType.getRegisterType(RegisterType.Category.Reference, elementClassDef)); - } else { - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, - RegisterType.getRegisterType(RegisterType.Category.Null, null)); } } - private void handle32BitPrimitiveAput(AnalyzedInstruction analyzedInstruction, + private void verify32BitPrimitiveAput(AnalyzedInstruction analyzedInstruction, RegisterType.Category instructionCategory) { ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; @@ -1976,7 +2432,7 @@ public class MethodAnalyzer { } } - private void handleAputWide(AnalyzedInstruction analyzedInstruction) { + private void verifyAputWide(AnalyzedInstruction analyzedInstruction) { ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), Primitive32BitCategories); @@ -2013,7 +2469,7 @@ public class MethodAnalyzer { } } - private void handleAputObject(AnalyzedInstruction analyzedInstruction) { + private void verifyAputObject(AnalyzedInstruction analyzedInstruction) { ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), Primitive32BitCategories); @@ -2052,7 +2508,13 @@ public class MethodAnalyzer { } } - private void handle32BitPrimitiveIget(AnalyzedInstruction analyzedInstruction, + private void analyze32BitPrimitiveIget(AnalyzedInstruction analyzedInstruction, + RegisterType.Category instructionCategory) { + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, + RegisterType.getRegisterType(instructionCategory, null)); + } + + private void verify32BitPrimitiveIget(AnalyzedInstruction analyzedInstruction, RegisterType.Category instructionCategory) { TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; @@ -2077,12 +2539,20 @@ public class MethodAnalyzer { "for the instruction.", analyzedInstruction.instruction.opcode.name, field.getFieldString())); } - - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, - RegisterType.getRegisterType(instructionCategory, null)); } - private void handleIgetWide(AnalyzedInstruction analyzedInstruction) { + private void analyzeIgetWideObject(AnalyzedInstruction analyzedInstruction) { + TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; + + Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); + assert referencedItem instanceof FieldIdItem; + FieldIdItem field = (FieldIdItem)referencedItem; + + RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, fieldType); + } + + private void verifyIgetWide(AnalyzedInstruction analyzedInstruction) { TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), @@ -2106,11 +2576,9 @@ public class MethodAnalyzer { "for the instruction.", analyzedInstruction.instruction.opcode.name, field.getFieldString())); } - - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, fieldType); } - private void handleIgetObject(AnalyzedInstruction analyzedInstruction) { + private void verifyIgetObject(AnalyzedInstruction analyzedInstruction) { TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), @@ -2134,11 +2602,9 @@ public class MethodAnalyzer { "for the instruction.", analyzedInstruction.instruction.opcode.name, field.getFieldString())); } - - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, fieldType); } - private void handle32BitPrimitiveIput(AnalyzedInstruction analyzedInstruction, + private void verify32BitPrimitiveIput(AnalyzedInstruction analyzedInstruction, RegisterType.Category instructionCategory) { TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; @@ -2183,7 +2649,7 @@ public class MethodAnalyzer { } } - private void handleIputWide(AnalyzedInstruction analyzedInstruction) { + private void verifyIputWide(AnalyzedInstruction analyzedInstruction) { TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), @@ -2211,7 +2677,7 @@ public class MethodAnalyzer { } } - private void handleIputObject(AnalyzedInstruction analyzedInstruction) { + private void verifyIputObject(AnalyzedInstruction analyzedInstruction) { TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), @@ -2248,7 +2714,13 @@ public class MethodAnalyzer { } } - private void handle32BitPrimitiveSget(AnalyzedInstruction analyzedInstruction, + private void analyze32BitPrimitiveSget(AnalyzedInstruction analyzedInstruction, + RegisterType.Category instructionCategory) { + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, + RegisterType.getRegisterType(instructionCategory, null)); + } + + private void verify32BitPrimitiveSget(AnalyzedInstruction analyzedInstruction, RegisterType.Category instructionCategory) { //TODO: check access Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); @@ -2262,12 +2734,18 @@ public class MethodAnalyzer { "for the instruction.", analyzedInstruction.instruction.opcode.name, field.getFieldString())); } - - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, - RegisterType.getRegisterType(instructionCategory, null)); } - private void handleSgetWide(AnalyzedInstruction analyzedInstruction) { + private void analyzeSgetWideObject(AnalyzedInstruction analyzedInstruction) { + Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); + assert referencedItem instanceof FieldIdItem; + FieldIdItem field = (FieldIdItem)referencedItem; + + RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, fieldType); + } + + private void verifySgetWide(AnalyzedInstruction analyzedInstruction) { //TODO: check access Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); assert referencedItem instanceof FieldIdItem; @@ -2283,11 +2761,9 @@ public class MethodAnalyzer { "for the instruction.", analyzedInstruction.instruction.opcode.name, field.getFieldString())); } - - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, fieldType); } - private void handleSgetObject(AnalyzedInstruction analyzedInstruction) { + private void verifySgetObject(AnalyzedInstruction analyzedInstruction) { //TODO: check access Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); assert referencedItem instanceof FieldIdItem; @@ -2300,11 +2776,9 @@ public class MethodAnalyzer { "for the instruction.", analyzedInstruction.instruction.opcode.name, field.getFieldString())); } - - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, fieldType); } - private void handle32BitPrimitiveSput(AnalyzedInstruction analyzedInstruction, + private void verify32BitPrimitiveSput(AnalyzedInstruction analyzedInstruction, RegisterType.Category instructionCategory) { SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; @@ -2339,7 +2813,7 @@ public class MethodAnalyzer { } } - private void handleSputWide(AnalyzedInstruction analyzedInstruction) { + private void verifySputWide(AnalyzedInstruction analyzedInstruction) { SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; @@ -2359,7 +2833,7 @@ public class MethodAnalyzer { } } - private void handleSputObject(AnalyzedInstruction analyzedInstruction) { + private void verifySputObject(AnalyzedInstruction analyzedInstruction) { SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; RegisterType sourceRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), @@ -2387,14 +2861,24 @@ public class MethodAnalyzer { } } - private void handleInvoke(AnalyzedInstruction analyzedInstruction, int invokeType) { + private void analyzeInvokeDirect(AnalyzedInstruction analyzedInstruction) { FiveRegisterInstruction instruction = (FiveRegisterInstruction)analyzedInstruction.instruction; - handleInvokeCommon(analyzedInstruction, false, invokeType, new Format35cRegisterIterator(instruction)); + analyzeInvokeDirectCommon(analyzedInstruction, new Format35cRegisterIterator(instruction)); } - private void handleInvokeRange(AnalyzedInstruction analyzedInstruction, int invokeType) { + private void verifyInvoke(AnalyzedInstruction analyzedInstruction, int invokeType) { + FiveRegisterInstruction instruction = (FiveRegisterInstruction)analyzedInstruction.instruction; + verifyInvokeCommon(analyzedInstruction, false, invokeType, new Format35cRegisterIterator(instruction)); + } + + private void analyzeInvokeDirectRange(AnalyzedInstruction analyzedInstruction) { RegisterRangeInstruction instruction = (RegisterRangeInstruction)analyzedInstruction.instruction; - handleInvokeCommon(analyzedInstruction, true, invokeType, new Format3rcRegisterIterator(instruction)); + analyzeInvokeDirectCommon(analyzedInstruction, new Format3rcRegisterIterator(instruction)); + } + + private void verifyInvokeRange(AnalyzedInstruction analyzedInstruction, int invokeType) { + RegisterRangeInstruction instruction = (RegisterRangeInstruction)analyzedInstruction.instruction; + verifyInvokeCommon(analyzedInstruction, true, invokeType, new Format3rcRegisterIterator(instruction)); } private static final int INVOKE_VIRTUAL = 0x01; @@ -2403,7 +2887,59 @@ public class MethodAnalyzer { private static final int INVOKE_INTERFACE = 0x08; private static final int INVOKE_STATIC = 0x10; - private void handleInvokeCommon(AnalyzedInstruction analyzedInstruction, boolean isRange, int invokeType, + private void analyzeInvokeDirectCommon(AnalyzedInstruction analyzedInstruction, RegisterIterator registers) { + //the only time that an invoke instruction changes a register type is when using invoke-direct on a + //constructor () method, which changes the uninitialized reference (and any register that the same + //uninit reference has been copied to) to an initialized reference + + InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; + + Item item = instruction.getReferencedItem(); + assert item.getItemType() == ItemType.TYPE_METHOD_ID_ITEM; + MethodIdItem methodIdItem = (MethodIdItem)item; + + if (!methodIdItem.getMethodName().getStringValue().equals("")) { + return; + } + + RegisterType objectRegisterType; + //the object register is always the first register + int objectRegister = registers.getRegister(); + + objectRegisterType = analyzedInstruction.getPreInstructionRegisterType(objectRegister); + assert objectRegisterType != null; + + if (objectRegisterType.category != RegisterType.Category.UninitRef && + objectRegisterType.category != RegisterType.Category.UninitThis) { + return; + } + + setPostRegisterTypeAndPropagateChanges(analyzedInstruction, objectRegister, + RegisterType.getRegisterType(RegisterType.Category.Reference, objectRegisterType.type)); + + for (int i=0; i= analyzedInstruction.postRegisterMap.length) { - throw new ValidationException(String.format("v%d is the last register and not a valid wide register " + - "pair.", registerNumber)); + throw new ValidationException(String.format("v%d cannot be used as the first register in a wide register" + + "pair because it is the last register.", registerNumber)); + } + } + + private static interface RegisterIterator { + int getRegister(); + boolean moveNext(); + int getCount(); + boolean pastEnd(); + } + + private static class Format35cRegisterIterator implements RegisterIterator { + private final int registerCount; + private final int[] registers; + private int currentRegister = 0; + + public Format35cRegisterIterator(FiveRegisterInstruction instruction) { + registerCount = instruction.getRegCount(); + registers = new int[]{instruction.getRegisterD(), instruction.getRegisterE(), + instruction.getRegisterF(), instruction.getRegisterG(), + instruction.getRegisterA()}; + } + + public int getRegister() { + return registers[currentRegister]; + } + + public boolean moveNext() { + currentRegister++; + return !pastEnd(); + } + + public int getCount() { + return registerCount; + } + + public boolean pastEnd() { + return currentRegister >= registerCount; + } + } + + private static class Format3rcRegisterIterator implements RegisterIterator { + private final int startRegister; + private final int registerCount; + private int currentRegister = 0; + + public Format3rcRegisterIterator(RegisterRangeInstruction instruction) { + startRegister = instruction.getStartRegister(); + registerCount = instruction.getRegCount(); + } + + public int getRegister() { + return startRegister + currentRegister; + } + + public boolean moveNext() { + currentRegister++; + return !pastEnd(); + } + + public int getCount() { + return registerCount; + } + + public boolean pastEnd() { + return currentRegister >= registerCount; } } }