AnalyzedInstruction.java revision 00fc68adf2e39aeb9fed35293f2576bbe729ec4b
1/* 2 * [The "BSD licence"] 3 * Copyright (c) 2010 Ben Gruver (JesusFreke) 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. The name of the author may not be used to endorse or promote products 15 * derived from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29package org.jf.dexlib.Code.Analysis; 30 31import org.jf.dexlib.Code.*; 32import org.jf.dexlib.Item; 33import org.jf.dexlib.ItemType; 34import org.jf.dexlib.MethodIdItem; 35import org.jf.dexlib.Util.ExceptionWithContext; 36 37import java.util.*; 38 39public class AnalyzedInstruction implements Comparable<AnalyzedInstruction> { 40 /** 41 * The actual instruction 42 */ 43 protected Instruction instruction; 44 45 /** 46 * The index of the instruction, where the first instruction in the method is at index 0, and so on 47 */ 48 protected final int instructionIndex; 49 50 /** 51 * Instructions that can pass on execution to this one during normal execution 52 */ 53 protected final TreeSet<AnalyzedInstruction> predecessors = new TreeSet<AnalyzedInstruction>(); 54 55 /** 56 * Instructions that can execution could pass on to next during normal execution 57 */ 58 protected final LinkedList<AnalyzedInstruction> successors = new LinkedList<AnalyzedInstruction>(); 59 60 /** 61 * This contains the register types *before* the instruction has executed 62 */ 63 protected final RegisterType[] preRegisterMap; 64 65 /** 66 * This contains the register types *after* the instruction has executed 67 */ 68 protected final RegisterType[] postRegisterMap; 69 70 /** 71 * When deodexing, we might need to deodex this instruction multiple times, when we merge in new register 72 * information. When this happens, we need to restore the original (odexed) instruction, so we can deodex it again 73 */ 74 protected final Instruction originalInstruction; 75 76 77 /** 78 * A dead instruction is one that is unreachable because it follows an odexed instruction that can't be deodexed 79 * because it's object register is always null. In the non-odexed code that the odex was generated from, we would 80 * have technically considered this code reachable and could verify it, even though the instruction that ended up 81 * being odexed was always null, because we would assume both "paths" out of the instruction are valid - the one 82 * where execution proceeds normally to the next instruction, and the one where an exception occurs and execution 83 * either goes to a catch block, or out of the method. 84 * 85 * However, in the odexed case, we can't verify the code following an undeodexable instruction because we lack 86 * the register information from the undeodexable instruction - because we don't know the actual method or field 87 * that is being accessed. 88 * 89 * The undeodexable instruction is guaranteed to throw an NPE, so the following code is effectivetly unreachable. 90 * Once we detect an undeodexeable instruction, the following code is marked as dead up until a non-dead execution 91 * path merges in. Additionally, we remove the predecessors/successors of any dead instruction. For example, if 92 * there is a dead goto instruction, then we would remove the target instruction as a successor, and we would 93 * also remove the dead goto instruction as a predecessor to the target. 94 */ 95 protected boolean dead = false; 96 97 public AnalyzedInstruction(Instruction instruction, int instructionIndex, int registerCount) { 98 this.instruction = instruction; 99 this.originalInstruction = instruction; 100 this.instructionIndex = instructionIndex; 101 this.postRegisterMap = new RegisterType[registerCount]; 102 this.preRegisterMap = new RegisterType[registerCount]; 103 RegisterType unknown = RegisterType.getRegisterType(RegisterType.Category.Unknown, null); 104 for (int i=0; i<registerCount; i++) { 105 preRegisterMap[i] = unknown; 106 postRegisterMap[i] = unknown; 107 } 108 } 109 110 public int getInstructionIndex() { 111 return instructionIndex; 112 } 113 114 public int getPredecessorCount() { 115 return predecessors.size(); 116 } 117 118 public SortedSet<AnalyzedInstruction> getPredecessors() { 119 return Collections.unmodifiableSortedSet(predecessors); 120 } 121 122 protected boolean addPredecessor(AnalyzedInstruction predecessor) { 123 return predecessors.add(predecessor); 124 } 125 126 protected void addSuccessor(AnalyzedInstruction successor) { 127 successors.add(successor); 128 } 129 130 protected void setDeodexedInstruction(Instruction instruction) { 131 assert originalInstruction.opcode.odexOnly(); 132 this.instruction = instruction; 133 } 134 135 protected void restoreOdexedInstruction() { 136 assert originalInstruction.opcode.odexOnly(); 137 instruction = originalInstruction; 138 } 139 140 public int getSuccessorCount() { 141 return successors.size(); 142 } 143 144 public List<AnalyzedInstruction> getSuccesors() { 145 return Collections.unmodifiableList(successors); 146 } 147 148 public Instruction getInstruction() { 149 return instruction; 150 } 151 152 public Instruction getOriginalInstruction() { 153 return originalInstruction; 154 } 155 156 public boolean isDead() { 157 return dead; 158 } 159 160 /** 161 * Is this instruction a "beginning instruction". A beginning instruction is defined to be an instruction 162 * that can be the first successfully executed instruction in the method. The first instruction is always a 163 * beginning instruction. If the first instruction can throw an exception, and is covered by a try block, then 164 * the first instruction of any exception handler for that try block is also a beginning instruction. And likewise, 165 * if any of those instructions can throw an exception and are covered by try blocks, the first instruction of the 166 * corresponding exception handler is a beginning instruction, etc. 167 * 168 * To determine this, we simply check if the first predecessor is the fake "StartOfMethod" instruction, which has 169 * an instruction index of -1. 170 * @return a boolean value indicating whether this instruction is a beginning instruction 171 */ 172 public boolean isBeginningInstruction() { 173 if (predecessors.size() == 0) { 174 return false; 175 } 176 177 if (predecessors.first().instructionIndex == -1) { 178 return true; 179 } 180 return false; 181 } 182 183 /* 184 * Merges the given register type into the specified pre-instruction register, and also sets the post-instruction 185 * register type accordingly if it isn't a destination register for this instruction 186 * @param registerNumber Which register to set 187 * @param registerType The register type 188 * @returns true If the post-instruction register type was changed. This might be false if either the specified 189 * register is a destination register for this instruction, or if the pre-instruction register type didn't change 190 * after merging in the given register type 191 */ 192 protected boolean mergeRegister(int registerNumber, RegisterType registerType, BitSet verifiedInstructions) { 193 assert registerNumber >= 0 && registerNumber < postRegisterMap.length; 194 assert registerType != null; 195 196 RegisterType oldRegisterType = preRegisterMap[registerNumber]; 197 RegisterType mergedRegisterType = oldRegisterType.merge(registerType); 198 199 if (mergedRegisterType == oldRegisterType) { 200 return false; 201 } 202 203 preRegisterMap[registerNumber] = mergedRegisterType; 204 verifiedInstructions.clear(instructionIndex); 205 206 if (!setsRegister(registerNumber)) { 207 postRegisterMap[registerNumber] = mergedRegisterType; 208 return true; 209 } 210 211 return false; 212 } 213 214 /** 215 * Iterates over the predecessors of this instruction, and merges all the post-instruction register types for the 216 * given register. Any dead, unreachable, or odexed predecessor is ignored 217 * @param registerNumber the register number 218 * @return The register type resulting from merging the post-instruction register types from all predecessors 219 */ 220 protected RegisterType mergePreRegisterTypeFromPredecessors(int registerNumber) { 221 RegisterType mergedRegisterType = null; 222 for (AnalyzedInstruction predecessor: predecessors) { 223 RegisterType predecessorRegisterType = predecessor.postRegisterMap[registerNumber]; 224 assert predecessorRegisterType != null; 225 mergedRegisterType = predecessorRegisterType.merge(mergedRegisterType); 226 } 227 return mergedRegisterType; 228 } 229 230 /* 231 * Sets the "post-instruction" register type as indicated. 232 * @param registerNumber Which register to set 233 * @param registerType The "post-instruction" register type 234 * @returns true if the given register type is different than the existing post-instruction register type 235 */ 236 protected boolean setPostRegisterType(int registerNumber, RegisterType registerType) { 237 assert registerNumber >= 0 && registerNumber < postRegisterMap.length; 238 assert registerType != null; 239 240 RegisterType oldRegisterType = postRegisterMap[registerNumber]; 241 if (oldRegisterType == registerType) { 242 return false; 243 } 244 245 postRegisterMap[registerNumber] = registerType; 246 return true; 247 } 248 249 250 protected boolean isInvokeInit() { 251 if (instruction == null || 252 (instruction.opcode != Opcode.INVOKE_DIRECT && instruction.opcode != Opcode.INVOKE_DIRECT_RANGE && 253 instruction.opcode != Opcode.INVOKE_DIRECT_EMPTY)) { 254 return false; 255 } 256 257 //TODO: check access flags instead of name? 258 259 InstructionWithReference instruction = (InstructionWithReference)this.instruction; 260 Item item = instruction.getReferencedItem(); 261 assert item.getItemType() == ItemType.TYPE_METHOD_ID_ITEM; 262 MethodIdItem method = (MethodIdItem)item; 263 264 if (!method.getMethodName().getStringValue().equals("<init>")) { 265 return false; 266 } 267 268 return true; 269 } 270 271 public boolean setsRegister() { 272 return instruction.opcode.setsRegister(); 273 } 274 275 public boolean setsWideRegister() { 276 return instruction.opcode.setsWideRegister(); 277 } 278 279 public boolean setsRegister(int registerNumber) { 280 //When constructing a new object, the register type will be an uninitialized reference after the new-instance 281 //instruction, but becomes an initialized reference once the <init> method is called. So even though invoke 282 //instructions don't normally change any registers, calling an <init> method will change the type of its 283 //object register. If the uninitialized reference has been copied to other registers, they will be initialized 284 //as well, so we need to check for that too 285 if (isInvokeInit()) { 286 int destinationRegister; 287 if (instruction instanceof FiveRegisterInstruction) { 288 destinationRegister = ((FiveRegisterInstruction)instruction).getRegisterD(); 289 } else { 290 assert instruction instanceof RegisterRangeInstruction; 291 RegisterRangeInstruction rangeInstruction = (RegisterRangeInstruction)instruction; 292 assert rangeInstruction.getRegCount() > 0; 293 destinationRegister = rangeInstruction.getStartRegister(); 294 } 295 296 if (registerNumber == destinationRegister) { 297 return true; 298 } 299 RegisterType preInstructionDestRegisterType = getPreInstructionRegisterType(registerNumber); 300 if (preInstructionDestRegisterType.category != RegisterType.Category.UninitRef && 301 preInstructionDestRegisterType.category != RegisterType.Category.UninitThis) { 302 303 return false; 304 } 305 //check if the uninit ref has been copied to another register 306 if (getPreInstructionRegisterType(registerNumber) == preInstructionDestRegisterType) { 307 return true; 308 } 309 return false; 310 } 311 312 if (!setsRegister()) { 313 return false; 314 } 315 int destinationRegister = getDestinationRegister(); 316 317 if (registerNumber == destinationRegister) { 318 return true; 319 } 320 if (setsWideRegister() && registerNumber == (destinationRegister + 1)) { 321 return true; 322 } 323 return false; 324 } 325 326 public int getDestinationRegister() { 327 if (!this.instruction.opcode.setsRegister()) { 328 throw new ExceptionWithContext("Cannot call getDestinationRegister() for an instruction that doesn't " + 329 "store a value"); 330 } 331 return ((SingleRegisterInstruction)instruction).getRegisterA(); 332 } 333 334 public int getRegisterCount() { 335 return postRegisterMap.length; 336 } 337 338 public RegisterType getPostInstructionRegisterType(int registerNumber) { 339 return postRegisterMap[registerNumber]; 340 } 341 342 public RegisterType getPreInstructionRegisterType(int registerNumber) { 343 return preRegisterMap[registerNumber]; 344 } 345 346 public int compareTo(AnalyzedInstruction analyzedInstruction) { 347 //TODO: out of curiosity, check the disassembly of this to see if it retrieves the value of analyzedInstruction.instructionIndex for every access. It should, because the field is final. What about if we set the field to non-final? 348 if (instructionIndex < analyzedInstruction.instructionIndex) { 349 return -1; 350 } else if (instructionIndex == analyzedInstruction.instructionIndex) { 351 return 0; 352 } else { 353 return 1; 354 } 355 } 356} 357 358