/* * Copyright 2013, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 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. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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.dexlib2.analysis; import org.jf.dexlib2.iface.instruction.*; import org.jf.dexlib2.iface.reference.MethodReference; import org.jf.dexlib2.iface.reference.Reference; import org.jf.util.ExceptionWithContext; import javax.annotation.Nonnull; import java.util.*; public class AnalyzedInstruction implements Comparable<AnalyzedInstruction> { /** * The actual instruction */ protected Instruction instruction; /** * The index of the instruction, where the first instruction in the method is at index 0, and so on */ protected final int instructionIndex; /** * Instructions that can pass on execution to this one during normal execution */ protected final TreeSet<AnalyzedInstruction> predecessors = new TreeSet<AnalyzedInstruction>(); /** * Instructions that can execution could pass on to next during normal execution */ protected final LinkedList<AnalyzedInstruction> successors = new LinkedList<AnalyzedInstruction>(); /** * This contains the register types *before* the instruction has executed */ protected final RegisterType[] preRegisterMap; /** * This contains the register types *after* the instruction has executed */ protected final RegisterType[] postRegisterMap; /** * When deodexing, we might need to deodex this instruction multiple times, when we merge in new register * information. When this happens, we need to restore the original (odexed) instruction, so we can deodex it again */ protected final Instruction originalInstruction; public AnalyzedInstruction(Instruction instruction, int instructionIndex, int registerCount) { this.instruction = instruction; this.originalInstruction = instruction; this.instructionIndex = instructionIndex; this.postRegisterMap = new RegisterType[registerCount]; this.preRegisterMap = new RegisterType[registerCount]; RegisterType unknown = RegisterType.getRegisterType(RegisterType.UNKNOWN, null); for (int i=0; i<registerCount; i++) { preRegisterMap[i] = unknown; postRegisterMap[i] = unknown; } } public int getInstructionIndex() { return instructionIndex; } public int getPredecessorCount() { return predecessors.size(); } public SortedSet<AnalyzedInstruction> getPredecessors() { return Collections.unmodifiableSortedSet(predecessors); } protected boolean addPredecessor(AnalyzedInstruction predecessor) { return predecessors.add(predecessor); } protected void addSuccessor(AnalyzedInstruction successor) { successors.add(successor); } protected void setDeodexedInstruction(Instruction instruction) { assert originalInstruction.getOpcode().odexOnly(); this.instruction = instruction; } protected void restoreOdexedInstruction() { assert originalInstruction.getOpcode().odexOnly(); instruction = originalInstruction; } public int getSuccessorCount() { return successors.size(); } public List<AnalyzedInstruction> getSuccesors() { return Collections.unmodifiableList(successors); } public Instruction getInstruction() { return instruction; } public Instruction getOriginalInstruction() { return originalInstruction; } /** * Is this instruction a "beginning instruction". A beginning instruction is defined to be an instruction * that can be the first successfully executed instruction in the method. The first instruction is always a * beginning instruction. If the first instruction can throw an exception, and is covered by a try block, then * the first instruction of any exception handler for that try block is also a beginning instruction. And likewise, * if any of those instructions can throw an exception and are covered by try blocks, the first instruction of the * corresponding exception handler is a beginning instruction, etc. * * To determine this, we simply check if the first predecessor is the fake "StartOfMethod" instruction, which has * an instruction index of -1. * @return a boolean value indicating whether this instruction is a beginning instruction */ public boolean isBeginningInstruction() { //if this instruction has no predecessors, it is either the fake "StartOfMethod" instruction or it is an //unreachable instruction. if (predecessors.size() == 0) { return false; } if (predecessors.first().instructionIndex == -1) { return true; } return false; } /* * Merges the given register type into the specified pre-instruction register, and also sets the post-instruction * register type accordingly if it isn't a destination register for this instruction * @param registerNumber Which register to set * @param registerType The register type * @returns true If the post-instruction register type was changed. This might be false if either the specified * register is a destination register for this instruction, or if the pre-instruction register type didn't change * after merging in the given register type */ protected boolean mergeRegister(int registerNumber, RegisterType registerType, BitSet verifiedInstructions) { assert registerNumber >= 0 && registerNumber < postRegisterMap.length; assert registerType != null; RegisterType oldRegisterType = preRegisterMap[registerNumber]; RegisterType mergedRegisterType = oldRegisterType.merge(registerType); if (mergedRegisterType.equals(oldRegisterType)) { return false; } preRegisterMap[registerNumber] = mergedRegisterType; verifiedInstructions.clear(instructionIndex); if (!setsRegister(registerNumber)) { postRegisterMap[registerNumber] = mergedRegisterType; return true; } return false; } /** * Iterates over the predecessors of this instruction, and merges all the post-instruction register types for the * given register. Any dead, unreachable, or odexed predecessor is ignored * @param registerNumber the register number * @return The register type resulting from merging the post-instruction register types from all predecessors */ protected RegisterType mergePreRegisterTypeFromPredecessors(int registerNumber) { RegisterType mergedRegisterType = null; for (AnalyzedInstruction predecessor: predecessors) { RegisterType predecessorRegisterType = predecessor.postRegisterMap[registerNumber]; assert predecessorRegisterType != null; mergedRegisterType = predecessorRegisterType.merge(mergedRegisterType); } return mergedRegisterType; } /* * Sets the "post-instruction" register type as indicated. * @param registerNumber Which register to set * @param registerType The "post-instruction" register type * @returns true if the given register type is different than the existing post-instruction register type */ protected boolean setPostRegisterType(int registerNumber, RegisterType registerType) { assert registerNumber >= 0 && registerNumber < postRegisterMap.length; assert registerType != null; RegisterType oldRegisterType = postRegisterMap[registerNumber]; if (oldRegisterType.equals(registerType)) { return false; } postRegisterMap[registerNumber] = registerType; return true; } protected boolean isInvokeInit() { if (instruction == null || !instruction.getOpcode().canInitializeReference()) { return false; } ReferenceInstruction instruction = (ReferenceInstruction)this.instruction; Reference reference = instruction.getReference(); if (reference instanceof MethodReference) { return ((MethodReference)reference).getName().equals("<init>"); } return false; } public boolean setsRegister() { return instruction.getOpcode().setsRegister(); } public boolean setsWideRegister() { return instruction.getOpcode().setsWideRegister(); } public boolean setsRegister(int registerNumber) { //When constructing a new object, the register type will be an uninitialized reference after the new-instance //instruction, but becomes an initialized reference once the <init> method is called. So even though invoke //instructions don't normally change any registers, calling an <init> method will change the type of its //object register. If the uninitialized reference has been copied to other registers, they will be initialized //as well, so we need to check for that too if (isInvokeInit()) { int destinationRegister; if (instruction instanceof FiveRegisterInstruction) { destinationRegister = ((FiveRegisterInstruction)instruction).getRegisterC(); } else { assert instruction instanceof RegisterRangeInstruction; RegisterRangeInstruction rangeInstruction = (RegisterRangeInstruction)instruction; assert rangeInstruction.getRegisterCount() > 0; destinationRegister = rangeInstruction.getStartRegister(); } if (registerNumber == destinationRegister) { return true; } RegisterType preInstructionDestRegisterType = getPreInstructionRegisterType(registerNumber); if (preInstructionDestRegisterType.category != RegisterType.UNINIT_REF && preInstructionDestRegisterType.category != RegisterType.UNINIT_THIS) { return false; } //check if the uninit ref has been copied to another register if (getPreInstructionRegisterType(registerNumber).equals(preInstructionDestRegisterType)) { return true; } return false; } if (!setsRegister()) { return false; } int destinationRegister = getDestinationRegister(); if (registerNumber == destinationRegister) { return true; } if (setsWideRegister() && registerNumber == (destinationRegister + 1)) { return true; } return false; } public int getDestinationRegister() { if (!this.instruction.getOpcode().setsRegister()) { throw new ExceptionWithContext("Cannot call getDestinationRegister() for an instruction that doesn't " + "store a value"); } return ((OneRegisterInstruction)instruction).getRegisterA(); } public int getRegisterCount() { return postRegisterMap.length; } @Nonnull public RegisterType getPostInstructionRegisterType(int registerNumber) { return postRegisterMap[registerNumber]; } @Nonnull public RegisterType getPreInstructionRegisterType(int registerNumber) { return preRegisterMap[registerNumber]; } public int compareTo(AnalyzedInstruction analyzedInstruction) { if (instructionIndex < analyzedInstruction.instructionIndex) { return -1; } else if (instructionIndex == analyzedInstruction.instructionIndex) { return 0; } else { return 1; } } }