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 29 package org.jf.dexlib.Code.Analysis; 30 31 import org.jf.dexlib.*; 32 import org.jf.dexlib.Code.*; 33 import org.jf.dexlib.Code.Format.*; 34 import org.jf.dexlib.Util.AccessFlags; 35 import org.jf.dexlib.Util.ExceptionWithContext; 36 import org.jf.dexlib.Util.SparseArray; 37 38 import java.util.BitSet; 39 import java.util.EnumSet; 40 import java.util.List; 41 42 /** 43 * The MethodAnalyzer performs several functions. It "analyzes" the instructions and infers the register types 44 * for each register, it can deodex odexed instructions, and it can verify the bytecode. The analysis and verification 45 * are done in two separate passes, because the analysis has to process instructions multiple times in some cases, and 46 * there's no need to perform the verification multiple times, so we wait until the method is fully analyzed and then 47 * verify it. 48 * 49 * Before calling the analyze() method, you must have initialized the ClassPath by calling 50 * ClassPath.InitializeClassPath 51 */ 52 public class MethodAnalyzer { 53 private final ClassDataItem.EncodedMethod encodedMethod; 54 55 private final DeodexUtil deodexUtil; 56 57 private SparseArray<AnalyzedInstruction> instructions; 58 59 private static final int NOT_ANALYZED = 0; 60 private static final int ANALYZED = 1; 61 private static final int VERIFIED = 2; 62 private int analyzerState = NOT_ANALYZED; 63 64 private BitSet analyzedInstructions; 65 66 private ValidationException validationException = null; 67 68 //This is a dummy instruction that occurs immediately before the first real instruction. We can initialize the 69 //register types for this instruction to the parameter types, in order to have them propagate to all of its 70 //successors, e.g. the first real instruction, the first instructions in any exception handlers covering the first 71 //instruction, etc. 72 private AnalyzedInstruction startOfMethod; 73 74 public MethodAnalyzer(ClassDataItem.EncodedMethod encodedMethod, boolean deodex) { 75 if (encodedMethod == null) { 76 throw new IllegalArgumentException("encodedMethod cannot be null"); 77 } 78 if (encodedMethod.codeItem == null || encodedMethod.codeItem.getInstructions().length == 0) { 79 throw new IllegalArgumentException("The method has no code"); 80 } 81 this.encodedMethod = encodedMethod; 82 83 if (deodex) { 84 this.deodexUtil = new DeodexUtil(encodedMethod.method.getDexFile()); 85 } else { 86 this.deodexUtil = null; 87 } 88 89 //override AnalyzedInstruction and provide custom implementations of some of the methods, so that we don't 90 //have to handle the case this special case of instruction being null, in the main class 91 startOfMethod = new AnalyzedInstruction(null, -1, encodedMethod.codeItem.getRegisterCount()) { 92 public boolean setsRegister() { 93 return false; 94 } 95 96 @Override 97 public boolean setsWideRegister() { 98 return false; 99 } 100 101 @Override 102 public boolean setsRegister(int registerNumber) { 103 return false; 104 } 105 106 @Override 107 public int getDestinationRegister() { 108 assert false; 109 return -1; 110 }; 111 }; 112 113 buildInstructionList(); 114 115 analyzedInstructions = new BitSet(instructions.size()); 116 } 117 118 public boolean isAnalyzed() { 119 return analyzerState >= ANALYZED; 120 } 121 122 public boolean isVerified() { 123 return analyzerState == VERIFIED; 124 } 125 126 public void analyze() { 127 assert encodedMethod != null; 128 assert encodedMethod.codeItem != null; 129 130 if (analyzerState >= ANALYZED) { 131 //the instructions have already been analyzed, so there is nothing to do 132 return; 133 } 134 135 CodeItem codeItem = encodedMethod.codeItem; 136 MethodIdItem methodIdItem = encodedMethod.method; 137 138 int totalRegisters = codeItem.getRegisterCount(); 139 int parameterRegisters = methodIdItem.getPrototype().getParameterRegisterCount(); 140 141 int nonParameterRegisters = totalRegisters - parameterRegisters; 142 143 for (AnalyzedInstruction instruction: instructions.getValues()) { 144 instruction.dead = true; 145 } 146 147 //if this isn't a static method, determine which register is the "this" register and set the type to the 148 //current class 149 if ((encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) == 0) { 150 nonParameterRegisters--; 151 int thisRegister = totalRegisters - parameterRegisters - 1; 152 153 //if this is a constructor, then set the "this" register to an uninitialized reference of the current class 154 if ((encodedMethod.accessFlags & AccessFlags.CONSTRUCTOR.getValue()) != 0) { 155 setPostRegisterTypeAndPropagateChanges(startOfMethod, thisRegister, 156 RegisterType.getRegisterType(RegisterType.Category.UninitThis, 157 ClassPath.getClassDef(methodIdItem.getContainingClass()))); 158 } else { 159 setPostRegisterTypeAndPropagateChanges(startOfMethod, thisRegister, 160 RegisterType.getRegisterType(RegisterType.Category.Reference, 161 ClassPath.getClassDef(methodIdItem.getContainingClass()))); 162 } 163 } 164 165 TypeListItem parameters = methodIdItem.getPrototype().getParameters(); 166 if (parameters != null) { 167 RegisterType[] parameterTypes = getParameterTypes(parameters, parameterRegisters); 168 for (int i=0; i<parameterTypes.length; i++) { 169 RegisterType registerType = parameterTypes[i]; 170 int registerNum = (totalRegisters - parameterRegisters) + i; 171 setPostRegisterTypeAndPropagateChanges(startOfMethod, registerNum, registerType); 172 } 173 } 174 175 RegisterType uninit = RegisterType.getRegisterType(RegisterType.Category.Uninit, null); 176 for (int i=0; i<nonParameterRegisters; i++) { 177 setPostRegisterTypeAndPropagateChanges(startOfMethod, i, uninit); 178 } 179 180 BitSet instructionsToAnalyze = new BitSet(instructions.size()); 181 182 //make sure all of the "first instructions" are marked for processing 183 for (AnalyzedInstruction successor: startOfMethod.successors) { 184 instructionsToAnalyze.set(successor.instructionIndex); 185 } 186 187 BitSet undeodexedInstructions = new BitSet(instructions.size()); 188 189 do { 190 boolean didSomething = false; 191 192 while (!instructionsToAnalyze.isEmpty()) { 193 for(int i=instructionsToAnalyze.nextSetBit(0); i>=0; i=instructionsToAnalyze.nextSetBit(i+1)) { 194 instructionsToAnalyze.clear(i); 195 if (analyzedInstructions.get(i)) { 196 continue; 197 } 198 AnalyzedInstruction instructionToAnalyze = instructions.valueAt(i); 199 instructionToAnalyze.dead = false; 200 try { 201 if (instructionToAnalyze.originalInstruction.opcode.odexOnly()) { 202 //if we had deodexed an odex instruction in a previous pass, we might have more specific 203 //register information now, so let's restore the original odexed instruction and 204 //re-deodex it 205 instructionToAnalyze.restoreOdexedInstruction(); 206 } 207 208 if (!analyzeInstruction(instructionToAnalyze)) { 209 undeodexedInstructions.set(i); 210 continue; 211 } else { 212 didSomething = true; 213 undeodexedInstructions.clear(i); 214 } 215 } catch (ValidationException ex) { 216 this.validationException = ex; 217 int codeAddress = getInstructionAddress(instructionToAnalyze); 218 ex.setCodeAddress(codeAddress); 219 ex.addContext(String.format("opcode: %s", instructionToAnalyze.instruction.opcode.name)); 220 ex.addContext(String.format("CodeAddress: %d", codeAddress)); 221 ex.addContext(String.format("Method: %s", encodedMethod.method.getMethodString())); 222 break; 223 } 224 225 analyzedInstructions.set(instructionToAnalyze.getInstructionIndex()); 226 227 for (AnalyzedInstruction successor: instructionToAnalyze.successors) { 228 instructionsToAnalyze.set(successor.getInstructionIndex()); 229 } 230 } 231 if (validationException != null) { 232 break; 233 } 234 } 235 236 if (!didSomething) { 237 break; 238 } 239 240 if (!undeodexedInstructions.isEmpty()) { 241 for (int i=undeodexedInstructions.nextSetBit(0); i>=0; i=undeodexedInstructions.nextSetBit(i+1)) { 242 instructionsToAnalyze.set(i); 243 } 244 } 245 } while (true); 246 247 for (int i=0; i<instructions.size(); i++) { 248 AnalyzedInstruction instruction = instructions.valueAt(i); 249 250 int objectRegisterNumber; 251 switch (instruction.getInstruction().getFormat()) { 252 case Format22cs: 253 objectRegisterNumber = ((Instruction22cs)instruction.instruction).getRegisterB(); 254 break; 255 case Format35ms: 256 objectRegisterNumber = ((Instruction35ms)instruction.instruction).getRegisterD(); 257 break; 258 case Format3rms: 259 objectRegisterNumber = ((Instruction3rms)instruction.instruction).getStartRegister(); 260 break; 261 default: 262 continue; 263 } 264 265 instruction.setDeodexedInstruction(new UnresolvedOdexInstruction(instruction.instruction, 266 objectRegisterNumber)); 267 } 268 269 analyzerState = ANALYZED; 270 } 271 272 public void verify() { 273 if (analyzerState < ANALYZED) { 274 throw new ExceptionWithContext("You must call analyze() before calling verify()."); 275 } 276 277 if (analyzerState == VERIFIED) { 278 //we've already verified the bytecode. nothing to do 279 return; 280 } 281 282 BitSet instructionsToVerify = new BitSet(instructions.size()); 283 BitSet verifiedInstructions = new BitSet(instructions.size()); 284 285 //make sure all of the "first instructions" are marked for processing 286 for (AnalyzedInstruction successor: startOfMethod.successors) { 287 instructionsToVerify.set(successor.instructionIndex); 288 } 289 290 while (!instructionsToVerify.isEmpty()) { 291 for (int i=instructionsToVerify.nextSetBit(0); i>=0; i=instructionsToVerify.nextSetBit(i+1)) { 292 instructionsToVerify.clear(i); 293 if (verifiedInstructions.get(i)) { 294 continue; 295 } 296 AnalyzedInstruction instructionToVerify = instructions.valueAt(i); 297 try { 298 verifyInstruction(instructionToVerify); 299 } catch (ValidationException ex) { 300 this.validationException = ex; 301 int codeAddress = getInstructionAddress(instructionToVerify); 302 ex.setCodeAddress(codeAddress); 303 ex.addContext(String.format("opcode: %s", instructionToVerify.instruction.opcode.name)); 304 ex.addContext(String.format("CodeAddress: %d", codeAddress)); 305 ex.addContext(String.format("Method: %s", encodedMethod.method.getMethodString())); 306 break; 307 } 308 309 verifiedInstructions.set(instructionToVerify.getInstructionIndex()); 310 311 for (AnalyzedInstruction successor: instructionToVerify.successors) { 312 instructionsToVerify.set(successor.getInstructionIndex()); 313 } 314 } 315 if (validationException != null) { 316 break; 317 } 318 } 319 320 analyzerState = VERIFIED; 321 } 322 323 private int getThisRegister() { 324 assert (encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) == 0; 325 326 CodeItem codeItem = encodedMethod.codeItem; 327 assert codeItem != null; 328 329 MethodIdItem methodIdItem = encodedMethod.method; 330 assert methodIdItem != null; 331 332 int totalRegisters = codeItem.getRegisterCount(); 333 if (totalRegisters == 0) { 334 throw new ValidationException("A non-static method must have at least 1 register"); 335 } 336 337 int parameterRegisters = methodIdItem.getPrototype().getParameterRegisterCount(); 338 339 return totalRegisters - parameterRegisters - 1; 340 } 341 342 private boolean isInstanceConstructor() { 343 return (encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) == 0 && 344 (encodedMethod.accessFlags & AccessFlags.CONSTRUCTOR.getValue()) != 0; 345 } 346 347 private boolean isStaticConstructor() { 348 return (encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) != 0 && 349 (encodedMethod.accessFlags & AccessFlags.CONSTRUCTOR.getValue()) != 0; 350 } 351 352 public AnalyzedInstruction getStartOfMethod() { 353 return startOfMethod; 354 } 355 356 /** 357 * @return a read-only list containing the instructions for tihs method. 358 */ 359 public List<AnalyzedInstruction> getInstructions() { 360 return instructions.getValues(); 361 } 362 363 public ClassDataItem.EncodedMethod getMethod() { 364 return this.encodedMethod; 365 } 366 367 public ValidationException getValidationException() { 368 return validationException; 369 } 370 371 private static RegisterType[] getParameterTypes(TypeListItem typeListItem, int parameterRegisterCount) { 372 assert typeListItem != null; 373 assert parameterRegisterCount == typeListItem.getRegisterCount(); 374 375 RegisterType[] registerTypes = new RegisterType[parameterRegisterCount]; 376 377 int registerNum = 0; 378 for (TypeIdItem type: typeListItem.getTypes()) { 379 if (type.getRegisterCount() == 2) { 380 registerTypes[registerNum++] = RegisterType.getWideRegisterTypeForTypeIdItem(type, true); 381 registerTypes[registerNum++] = RegisterType.getWideRegisterTypeForTypeIdItem(type, false); 382 } else { 383 registerTypes[registerNum++] = RegisterType.getRegisterTypeForTypeIdItem(type); 384 } 385 } 386 387 return registerTypes; 388 } 389 390 public int getInstructionAddress(AnalyzedInstruction instruction) { 391 return instructions.keyAt(instruction.instructionIndex); 392 } 393 394 private void setDestinationRegisterTypeAndPropagateChanges(AnalyzedInstruction analyzedInstruction, 395 RegisterType registerType) { 396 setPostRegisterTypeAndPropagateChanges(analyzedInstruction, analyzedInstruction.getDestinationRegister(), 397 registerType); 398 } 399 400 private void setPostRegisterTypeAndPropagateChanges(AnalyzedInstruction analyzedInstruction, int registerNumber, 401 RegisterType registerType) { 402 403 BitSet changedInstructions = new BitSet(instructions.size()); 404 405 if (!analyzedInstruction.setPostRegisterType(registerNumber, registerType)) { 406 return; 407 } 408 409 propagateRegisterToSuccessors(analyzedInstruction, registerNumber, changedInstructions); 410 411 //Using a for loop inside the while loop optimizes for the common case of the successors of an instruction 412 //occurring after the instruction. Any successors that occur prior to the instruction will be picked up on 413 //the next iteration of the while loop. 414 //This could also be done recursively, but in large methods it would likely cause very deep recursion, 415 //which requires the user to specify a larger stack size. This isn't really a problem, but it is slightly 416 //annoying. 417 while (!changedInstructions.isEmpty()) { 418 for (int instructionIndex=changedInstructions.nextSetBit(0); 419 instructionIndex>=0; 420 instructionIndex=changedInstructions.nextSetBit(instructionIndex+1)) { 421 422 changedInstructions.clear(instructionIndex); 423 424 propagateRegisterToSuccessors(instructions.valueAt(instructionIndex), registerNumber, 425 changedInstructions); 426 } 427 } 428 429 if (registerType.category == RegisterType.Category.LongLo) { 430 checkWidePair(registerNumber, analyzedInstruction); 431 setPostRegisterTypeAndPropagateChanges(analyzedInstruction, registerNumber+1, 432 RegisterType.getRegisterType(RegisterType.Category.LongHi, null)); 433 } else if (registerType.category == RegisterType.Category.DoubleLo) { 434 checkWidePair(registerNumber, analyzedInstruction); 435 setPostRegisterTypeAndPropagateChanges(analyzedInstruction, registerNumber+1, 436 RegisterType.getRegisterType(RegisterType.Category.DoubleHi, null)); 437 } 438 } 439 440 private void propagateRegisterToSuccessors(AnalyzedInstruction instruction, int registerNumber, 441 BitSet changedInstructions) { 442 RegisterType postRegisterType = instruction.getPostInstructionRegisterType(registerNumber); 443 for (AnalyzedInstruction successor: instruction.successors) { 444 if (successor.mergeRegister(registerNumber, postRegisterType, analyzedInstructions)) { 445 changedInstructions.set(successor.instructionIndex); 446 } 447 } 448 } 449 450 private void buildInstructionList() { 451 assert encodedMethod != null; 452 assert encodedMethod.codeItem != null; 453 int registerCount = encodedMethod.codeItem.getRegisterCount(); 454 455 Instruction[] insns = encodedMethod.codeItem.getInstructions(); 456 457 instructions = new SparseArray<AnalyzedInstruction>(insns.length); 458 459 //first, create all the instructions and populate the instructionAddresses array 460 int currentCodeAddress = 0; 461 for (int i=0; i<insns.length; i++) { 462 instructions.append(currentCodeAddress, new AnalyzedInstruction(insns[i], i, registerCount)); 463 assert instructions.indexOfKey(currentCodeAddress) == i; 464 currentCodeAddress += insns[i].getSize(currentCodeAddress); 465 } 466 467 //next, populate the exceptionHandlers array. The array item for each instruction that can throw an exception 468 //and is covered by a try block should be set to a list of the first instructions of each exception handler 469 //for the try block covering the instruction 470 CodeItem.TryItem[] tries = encodedMethod.codeItem.getTries(); 471 int triesIndex = 0; 472 CodeItem.TryItem currentTry = null; 473 AnalyzedInstruction[] currentExceptionHandlers = null; 474 AnalyzedInstruction[][] exceptionHandlers = new AnalyzedInstruction[insns.length][]; 475 476 if (tries != null) { 477 for (int i=0; i<instructions.size(); i++) { 478 AnalyzedInstruction instruction = instructions.valueAt(i); 479 Opcode instructionOpcode = instruction.instruction.opcode; 480 currentCodeAddress = getInstructionAddress(instruction); 481 482 //check if we have gone past the end of the current try 483 if (currentTry != null) { 484 if (currentTry.getStartCodeAddress() + currentTry.getTryLength() <= currentCodeAddress) { 485 currentTry = null; 486 triesIndex++; 487 } 488 } 489 490 //check if the next try is applicable yet 491 if (currentTry == null && triesIndex < tries.length) { 492 CodeItem.TryItem tryItem = tries[triesIndex]; 493 if (tryItem.getStartCodeAddress() <= currentCodeAddress) { 494 assert(tryItem.getStartCodeAddress() + tryItem.getTryLength() > currentCodeAddress); 495 496 currentTry = tryItem; 497 498 currentExceptionHandlers = buildExceptionHandlerArray(tryItem); 499 } 500 } 501 502 //if we're inside a try block, and the instruction can throw an exception, then add the exception handlers 503 //for the current instruction 504 if (currentTry != null && instructionOpcode.canThrow()) { 505 exceptionHandlers[i] = currentExceptionHandlers; 506 } 507 } 508 } 509 510 //finally, populate the successors and predecessors for each instruction. We start at the fake "StartOfMethod" 511 //instruction and follow the execution path. Any unreachable code won't have any predecessors or successors, 512 //and no reachable code will have an unreachable predessor or successor 513 assert instructions.size() > 0; 514 BitSet instructionsToProcess = new BitSet(insns.length); 515 516 addPredecessorSuccessor(startOfMethod, instructions.valueAt(0), exceptionHandlers, instructionsToProcess); 517 while (!instructionsToProcess.isEmpty()) { 518 int currentInstructionIndex = instructionsToProcess.nextSetBit(0); 519 instructionsToProcess.clear(currentInstructionIndex); 520 521 AnalyzedInstruction instruction = instructions.valueAt(currentInstructionIndex); 522 Opcode instructionOpcode = instruction.instruction.opcode; 523 int instructionCodeAddress = getInstructionAddress(instruction); 524 525 if (instruction.instruction.opcode.canContinue()) { 526 if (instruction.instruction.opcode != Opcode.NOP || 527 !instruction.instruction.getFormat().variableSizeFormat) { 528 529 if (currentInstructionIndex == instructions.size() - 1) { 530 throw new ValidationException("Execution can continue past the last instruction"); 531 } 532 533 AnalyzedInstruction nextInstruction = instructions.valueAt(currentInstructionIndex+1); 534 addPredecessorSuccessor(instruction, nextInstruction, exceptionHandlers, instructionsToProcess); 535 } 536 } 537 538 if (instruction.instruction instanceof OffsetInstruction) { 539 OffsetInstruction offsetInstruction = (OffsetInstruction)instruction.instruction; 540 541 if (instructionOpcode == Opcode.PACKED_SWITCH || instructionOpcode == Opcode.SPARSE_SWITCH) { 542 MultiOffsetInstruction switchDataInstruction = 543 (MultiOffsetInstruction)instructions.get(instructionCodeAddress + 544 offsetInstruction.getTargetAddressOffset()).instruction; 545 for (int targetAddressOffset: switchDataInstruction.getTargets()) { 546 AnalyzedInstruction targetInstruction = instructions.get(instructionCodeAddress + 547 targetAddressOffset); 548 549 addPredecessorSuccessor(instruction, targetInstruction, exceptionHandlers, 550 instructionsToProcess); 551 } 552 } else { 553 int targetAddressOffset = offsetInstruction.getTargetAddressOffset(); 554 AnalyzedInstruction targetInstruction = instructions.get(instructionCodeAddress + 555 targetAddressOffset); 556 addPredecessorSuccessor(instruction, targetInstruction, exceptionHandlers, instructionsToProcess); 557 } 558 } 559 } 560 } 561 562 private void addPredecessorSuccessor(AnalyzedInstruction predecessor, AnalyzedInstruction successor, 563 AnalyzedInstruction[][] exceptionHandlers, 564 BitSet instructionsToProcess) { 565 addPredecessorSuccessor(predecessor, successor, exceptionHandlers, instructionsToProcess, false); 566 } 567 568 private void addPredecessorSuccessor(AnalyzedInstruction predecessor, AnalyzedInstruction successor, 569 AnalyzedInstruction[][] exceptionHandlers, 570 BitSet instructionsToProcess, boolean allowMoveException) { 571 572 if (!allowMoveException && successor.instruction.opcode == Opcode.MOVE_EXCEPTION) { 573 throw new ValidationException("Execution can pass from the " + predecessor.instruction.opcode.name + 574 " instruction at code address 0x" + Integer.toHexString(getInstructionAddress(predecessor)) + 575 " to the move-exception instruction at address 0x" + 576 Integer.toHexString(getInstructionAddress(successor))); 577 } 578 579 if (!successor.addPredecessor(predecessor)) { 580 return; 581 } 582 583 predecessor.addSuccessor(successor); 584 instructionsToProcess.set(successor.getInstructionIndex()); 585 586 587 //if the successor can throw an instruction, then we need to add the exception handlers as additional 588 //successors to the predecessor (and then apply this same logic recursively if needed) 589 //Technically, we should handle the monitor-exit instruction as a special case. The exception is actually 590 //thrown *after* the instruction executes, instead of "before" the instruction executes, lke for any other 591 //instruction. But since it doesn't modify any registers, we can treat it like any other instruction. 592 AnalyzedInstruction[] exceptionHandlersForSuccessor = exceptionHandlers[successor.instructionIndex]; 593 if (exceptionHandlersForSuccessor != null) { 594 //the item for this instruction in exceptionHandlersForSuccessor should only be set if this instruction 595 //can throw an exception 596 assert successor.instruction.opcode.canThrow(); 597 598 for (AnalyzedInstruction exceptionHandler: exceptionHandlersForSuccessor) { 599 addPredecessorSuccessor(predecessor, exceptionHandler, exceptionHandlers, instructionsToProcess, true); 600 } 601 } 602 } 603 604 private AnalyzedInstruction[] buildExceptionHandlerArray(CodeItem.TryItem tryItem) { 605 int exceptionHandlerCount = tryItem.encodedCatchHandler.handlers.length; 606 int catchAllHandler = tryItem.encodedCatchHandler.getCatchAllHandlerAddress(); 607 if (catchAllHandler != -1) { 608 exceptionHandlerCount++; 609 } 610 611 AnalyzedInstruction[] exceptionHandlers = new AnalyzedInstruction[exceptionHandlerCount]; 612 for (int i=0; i<tryItem.encodedCatchHandler.handlers.length; i++) { 613 exceptionHandlers[i] = instructions.get(tryItem.encodedCatchHandler.handlers[i].getHandlerAddress()); 614 } 615 616 if (catchAllHandler != -1) { 617 exceptionHandlers[exceptionHandlers.length - 1] = instructions.get(catchAllHandler); 618 } 619 620 return exceptionHandlers; 621 } 622 623 /** 624 * @return false if analyzedInstruction is an odex instruction that couldn't be deodexed, due to its 625 * object register being null 626 */ 627 private boolean analyzeInstruction(AnalyzedInstruction analyzedInstruction) { 628 Instruction instruction = analyzedInstruction.instruction; 629 630 switch (instruction.opcode) { 631 case NOP: 632 return true; 633 case MOVE: 634 case MOVE_FROM16: 635 case MOVE_16: 636 case MOVE_WIDE: 637 case MOVE_WIDE_FROM16: 638 case MOVE_WIDE_16: 639 case MOVE_OBJECT: 640 case MOVE_OBJECT_FROM16: 641 case MOVE_OBJECT_16: 642 analyzeMove(analyzedInstruction); 643 return true; 644 case MOVE_RESULT: 645 case MOVE_RESULT_WIDE: 646 case MOVE_RESULT_OBJECT: 647 analyzeMoveResult(analyzedInstruction); 648 return true; 649 case MOVE_EXCEPTION: 650 analyzeMoveException(analyzedInstruction); 651 return true; 652 case RETURN_VOID: 653 case RETURN: 654 case RETURN_WIDE: 655 case RETURN_OBJECT: 656 return true; 657 case CONST_4: 658 case CONST_16: 659 case CONST: 660 analyzeConst(analyzedInstruction); 661 return true; 662 case CONST_HIGH16: 663 analyzeConstHigh16(analyzedInstruction); 664 return true; 665 case CONST_WIDE_16: 666 case CONST_WIDE_32: 667 case CONST_WIDE: 668 case CONST_WIDE_HIGH16: 669 analyzeWideConst(analyzedInstruction); 670 return true; 671 case CONST_STRING: 672 case CONST_STRING_JUMBO: 673 analyzeConstString(analyzedInstruction); 674 return true; 675 case CONST_CLASS: 676 analyzeConstClass(analyzedInstruction); 677 return true; 678 case MONITOR_ENTER: 679 case MONITOR_EXIT: 680 return true; 681 case CHECK_CAST: 682 analyzeCheckCast(analyzedInstruction); 683 return true; 684 case INSTANCE_OF: 685 analyzeInstanceOf(analyzedInstruction); 686 return true; 687 case ARRAY_LENGTH: 688 analyzeArrayLength(analyzedInstruction); 689 return true; 690 case NEW_INSTANCE: 691 analyzeNewInstance(analyzedInstruction); 692 return true; 693 case NEW_ARRAY: 694 analyzeNewArray(analyzedInstruction); 695 return true; 696 case FILLED_NEW_ARRAY: 697 case FILLED_NEW_ARRAY_RANGE: 698 return true; 699 case FILL_ARRAY_DATA: 700 analyzeArrayDataOrSwitch(analyzedInstruction); 701 case THROW: 702 case GOTO: 703 case GOTO_16: 704 case GOTO_32: 705 return true; 706 case PACKED_SWITCH: 707 case SPARSE_SWITCH: 708 analyzeArrayDataOrSwitch(analyzedInstruction); 709 return true; 710 case CMPL_FLOAT: 711 case CMPG_FLOAT: 712 case CMPL_DOUBLE: 713 case CMPG_DOUBLE: 714 case CMP_LONG: 715 analyzeFloatWideCmp(analyzedInstruction); 716 return true; 717 case IF_EQ: 718 case IF_NE: 719 case IF_LT: 720 case IF_GE: 721 case IF_GT: 722 case IF_LE: 723 case IF_EQZ: 724 case IF_NEZ: 725 case IF_LTZ: 726 case IF_GEZ: 727 case IF_GTZ: 728 case IF_LEZ: 729 return true; 730 case AGET: 731 analyze32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Integer); 732 return true; 733 case AGET_BOOLEAN: 734 analyze32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Boolean); 735 return true; 736 case AGET_BYTE: 737 analyze32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Byte); 738 return true; 739 case AGET_CHAR: 740 analyze32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Char); 741 return true; 742 case AGET_SHORT: 743 analyze32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Short); 744 return true; 745 case AGET_WIDE: 746 analyzeAgetWide(analyzedInstruction); 747 return true; 748 case AGET_OBJECT: 749 analyzeAgetObject(analyzedInstruction); 750 return true; 751 case APUT: 752 case APUT_BOOLEAN: 753 case APUT_BYTE: 754 case APUT_CHAR: 755 case APUT_SHORT: 756 case APUT_WIDE: 757 case APUT_OBJECT: 758 return true; 759 case IGET: 760 analyze32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Integer); 761 return true; 762 case IGET_BOOLEAN: 763 analyze32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Boolean); 764 return true; 765 case IGET_BYTE: 766 analyze32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Byte); 767 return true; 768 case IGET_CHAR: 769 analyze32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Char); 770 return true; 771 case IGET_SHORT: 772 analyze32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Short); 773 return true; 774 case IGET_WIDE: 775 case IGET_OBJECT: 776 analyzeIgetWideObject(analyzedInstruction); 777 return true; 778 case IPUT: 779 case IPUT_BOOLEAN: 780 case IPUT_BYTE: 781 case IPUT_CHAR: 782 case IPUT_SHORT: 783 case IPUT_WIDE: 784 case IPUT_OBJECT: 785 return true; 786 case SGET: 787 analyze32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Integer); 788 return true; 789 case SGET_BOOLEAN: 790 analyze32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Boolean); 791 return true; 792 case SGET_BYTE: 793 analyze32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Byte); 794 return true; 795 case SGET_CHAR: 796 analyze32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Char); 797 return true; 798 case SGET_SHORT: 799 analyze32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Short); 800 return true; 801 case SGET_WIDE: 802 case SGET_OBJECT: 803 analyzeSgetWideObject(analyzedInstruction); 804 return true; 805 case SPUT: 806 case SPUT_BOOLEAN: 807 case SPUT_BYTE: 808 case SPUT_CHAR: 809 case SPUT_SHORT: 810 case SPUT_WIDE: 811 case SPUT_OBJECT: 812 return true; 813 case INVOKE_VIRTUAL: 814 case INVOKE_SUPER: 815 return true; 816 case INVOKE_DIRECT: 817 analyzeInvokeDirect(analyzedInstruction); 818 return true; 819 case INVOKE_STATIC: 820 case INVOKE_INTERFACE: 821 case INVOKE_VIRTUAL_RANGE: 822 case INVOKE_SUPER_RANGE: 823 return true; 824 case INVOKE_DIRECT_RANGE: 825 analyzeInvokeDirectRange(analyzedInstruction); 826 return true; 827 case INVOKE_STATIC_RANGE: 828 case INVOKE_INTERFACE_RANGE: 829 return true; 830 case NEG_INT: 831 case NOT_INT: 832 analyzeUnaryOp(analyzedInstruction, RegisterType.Category.Integer); 833 return true; 834 case NEG_LONG: 835 case NOT_LONG: 836 analyzeUnaryOp(analyzedInstruction, RegisterType.Category.LongLo); 837 return true; 838 case NEG_FLOAT: 839 analyzeUnaryOp(analyzedInstruction, RegisterType.Category.Float); 840 return true; 841 case NEG_DOUBLE: 842 analyzeUnaryOp(analyzedInstruction, RegisterType.Category.DoubleLo); 843 return true; 844 case INT_TO_LONG: 845 analyzeUnaryOp(analyzedInstruction, RegisterType.Category.LongLo); 846 return true; 847 case INT_TO_FLOAT: 848 analyzeUnaryOp(analyzedInstruction, RegisterType.Category.Float); 849 return true; 850 case INT_TO_DOUBLE: 851 analyzeUnaryOp(analyzedInstruction, RegisterType.Category.DoubleLo); 852 return true; 853 case LONG_TO_INT: 854 case DOUBLE_TO_INT: 855 analyzeUnaryOp(analyzedInstruction, RegisterType.Category.Integer); 856 return true; 857 case LONG_TO_FLOAT: 858 case DOUBLE_TO_FLOAT: 859 analyzeUnaryOp(analyzedInstruction, RegisterType.Category.Float); 860 return true; 861 case LONG_TO_DOUBLE: 862 analyzeUnaryOp(analyzedInstruction, RegisterType.Category.DoubleLo); 863 return true; 864 case FLOAT_TO_INT: 865 analyzeUnaryOp(analyzedInstruction, RegisterType.Category.Integer); 866 return true; 867 case FLOAT_TO_LONG: 868 analyzeUnaryOp(analyzedInstruction, RegisterType.Category.LongLo); 869 return true; 870 case FLOAT_TO_DOUBLE: 871 analyzeUnaryOp(analyzedInstruction, RegisterType.Category.DoubleLo); 872 return true; 873 case DOUBLE_TO_LONG: 874 analyzeUnaryOp(analyzedInstruction, RegisterType.Category.LongLo); 875 return true; 876 case INT_TO_BYTE: 877 analyzeUnaryOp(analyzedInstruction, RegisterType.Category.Byte); 878 return true; 879 case INT_TO_CHAR: 880 analyzeUnaryOp(analyzedInstruction, RegisterType.Category.Char); 881 return true; 882 case INT_TO_SHORT: 883 analyzeUnaryOp(analyzedInstruction, RegisterType.Category.Short); 884 return true; 885 case ADD_INT: 886 case SUB_INT: 887 case MUL_INT: 888 case DIV_INT: 889 case REM_INT: 890 case SHL_INT: 891 case SHR_INT: 892 case USHR_INT: 893 analyzeBinaryOp(analyzedInstruction, RegisterType.Category.Integer, false); 894 return true; 895 case AND_INT: 896 case OR_INT: 897 case XOR_INT: 898 analyzeBinaryOp(analyzedInstruction, RegisterType.Category.Integer, true); 899 return true; 900 case ADD_LONG: 901 case SUB_LONG: 902 case MUL_LONG: 903 case DIV_LONG: 904 case REM_LONG: 905 case AND_LONG: 906 case OR_LONG: 907 case XOR_LONG: 908 case SHL_LONG: 909 case SHR_LONG: 910 case USHR_LONG: 911 analyzeBinaryOp(analyzedInstruction, RegisterType.Category.LongLo, false); 912 return true; 913 case ADD_FLOAT: 914 case SUB_FLOAT: 915 case MUL_FLOAT: 916 case DIV_FLOAT: 917 case REM_FLOAT: 918 analyzeBinaryOp(analyzedInstruction, RegisterType.Category.Float, false); 919 return true; 920 case ADD_DOUBLE: 921 case SUB_DOUBLE: 922 case MUL_DOUBLE: 923 case DIV_DOUBLE: 924 case REM_DOUBLE: 925 analyzeBinaryOp(analyzedInstruction, RegisterType.Category.DoubleLo, false); 926 return true; 927 case ADD_INT_2ADDR: 928 case SUB_INT_2ADDR: 929 case MUL_INT_2ADDR: 930 case DIV_INT_2ADDR: 931 case REM_INT_2ADDR: 932 case SHL_INT_2ADDR: 933 case SHR_INT_2ADDR: 934 case USHR_INT_2ADDR: 935 analyzeBinary2AddrOp(analyzedInstruction, RegisterType.Category.Integer, false); 936 return true; 937 case AND_INT_2ADDR: 938 case OR_INT_2ADDR: 939 case XOR_INT_2ADDR: 940 analyzeBinary2AddrOp(analyzedInstruction, RegisterType.Category.Integer, true); 941 return true; 942 case ADD_LONG_2ADDR: 943 case SUB_LONG_2ADDR: 944 case MUL_LONG_2ADDR: 945 case DIV_LONG_2ADDR: 946 case REM_LONG_2ADDR: 947 case AND_LONG_2ADDR: 948 case OR_LONG_2ADDR: 949 case XOR_LONG_2ADDR: 950 case SHL_LONG_2ADDR: 951 case SHR_LONG_2ADDR: 952 case USHR_LONG_2ADDR: 953 analyzeBinary2AddrOp(analyzedInstruction, RegisterType.Category.LongLo, false); 954 return true; 955 case ADD_FLOAT_2ADDR: 956 case SUB_FLOAT_2ADDR: 957 case MUL_FLOAT_2ADDR: 958 case DIV_FLOAT_2ADDR: 959 case REM_FLOAT_2ADDR: 960 analyzeBinary2AddrOp(analyzedInstruction, RegisterType.Category.Float, false); 961 return true; 962 case ADD_DOUBLE_2ADDR: 963 case SUB_DOUBLE_2ADDR: 964 case MUL_DOUBLE_2ADDR: 965 case DIV_DOUBLE_2ADDR: 966 case REM_DOUBLE_2ADDR: 967 analyzeBinary2AddrOp(analyzedInstruction, RegisterType.Category.DoubleLo, false); 968 return true; 969 case ADD_INT_LIT16: 970 case RSUB_INT: 971 case MUL_INT_LIT16: 972 case DIV_INT_LIT16: 973 case REM_INT_LIT16: 974 analyzeLiteralBinaryOp(analyzedInstruction, RegisterType.Category.Integer, false); 975 return true; 976 case AND_INT_LIT16: 977 case OR_INT_LIT16: 978 case XOR_INT_LIT16: 979 analyzeLiteralBinaryOp(analyzedInstruction, RegisterType.Category.Integer, true); 980 return true; 981 case ADD_INT_LIT8: 982 case RSUB_INT_LIT8: 983 case MUL_INT_LIT8: 984 case DIV_INT_LIT8: 985 case REM_INT_LIT8: 986 case SHL_INT_LIT8: 987 analyzeLiteralBinaryOp(analyzedInstruction, RegisterType.Category.Integer, false); 988 return true; 989 case AND_INT_LIT8: 990 case OR_INT_LIT8: 991 case XOR_INT_LIT8: 992 analyzeLiteralBinaryOp(analyzedInstruction, RegisterType.Category.Integer, true); 993 return true; 994 case SHR_INT_LIT8: 995 analyzeLiteralBinaryOp(analyzedInstruction, getDestTypeForLiteralShiftRight(analyzedInstruction, true), 996 false); 997 return true; 998 case USHR_INT_LIT8: 999 analyzeLiteralBinaryOp(analyzedInstruction, getDestTypeForLiteralShiftRight(analyzedInstruction, false), 1000 false); 1001 return true; 1002 1003 /*odexed instructions*/ 1004 case IGET_VOLATILE: 1005 case IPUT_VOLATILE: 1006 case SGET_VOLATILE: 1007 case SPUT_VOLATILE: 1008 case IGET_OBJECT_VOLATILE: 1009 case IGET_WIDE_VOLATILE: 1010 case IPUT_WIDE_VOLATILE: 1011 case SGET_WIDE_VOLATILE: 1012 case SPUT_WIDE_VOLATILE: 1013 analyzePutGetVolatile(analyzedInstruction); 1014 return true; 1015 case EXECUTE_INLINE: 1016 analyzeExecuteInline(analyzedInstruction); 1017 return true; 1018 case EXECUTE_INLINE_RANGE: 1019 analyzeExecuteInlineRange(analyzedInstruction); 1020 return true; 1021 case INVOKE_DIRECT_EMPTY: 1022 analyzeInvokeDirectEmpty(analyzedInstruction); 1023 return true; 1024 case IGET_QUICK: 1025 case IGET_WIDE_QUICK: 1026 case IGET_OBJECT_QUICK: 1027 case IPUT_QUICK: 1028 case IPUT_WIDE_QUICK: 1029 case IPUT_OBJECT_QUICK: 1030 return analyzeIputIgetQuick(analyzedInstruction); 1031 case INVOKE_VIRTUAL_QUICK: 1032 return analyzeInvokeVirtualQuick(analyzedInstruction, false, false); 1033 case INVOKE_SUPER_QUICK: 1034 return analyzeInvokeVirtualQuick(analyzedInstruction, true, false); 1035 case INVOKE_VIRTUAL_QUICK_RANGE: 1036 return analyzeInvokeVirtualQuick(analyzedInstruction, false, true); 1037 case INVOKE_SUPER_QUICK_RANGE: 1038 return analyzeInvokeVirtualQuick(analyzedInstruction, true, true); 1039 case IPUT_OBJECT_VOLATILE: 1040 case SGET_OBJECT_VOLATILE: 1041 case SPUT_OBJECT_VOLATILE: 1042 analyzePutGetVolatile(analyzedInstruction); 1043 return true; 1044 default: 1045 assert false; 1046 return true; 1047 } 1048 } 1049 1050 1051 private void verifyInstruction(AnalyzedInstruction analyzedInstruction) { 1052 Instruction instruction = analyzedInstruction.instruction; 1053 1054 switch (instruction.opcode) { 1055 case NOP: 1056 return; 1057 case MOVE: 1058 case MOVE_FROM16: 1059 case MOVE_16: 1060 verifyMove(analyzedInstruction, Primitive32BitCategories); 1061 return; 1062 case MOVE_WIDE: 1063 case MOVE_WIDE_FROM16: 1064 case MOVE_WIDE_16: 1065 verifyMove(analyzedInstruction, WideLowCategories); 1066 return; 1067 case MOVE_OBJECT: 1068 case MOVE_OBJECT_FROM16: 1069 case MOVE_OBJECT_16: 1070 verifyMove(analyzedInstruction, ReferenceOrUninitCategories); 1071 return; 1072 case MOVE_RESULT: 1073 verifyMoveResult(analyzedInstruction, Primitive32BitCategories); 1074 return; 1075 case MOVE_RESULT_WIDE: 1076 verifyMoveResult(analyzedInstruction, WideLowCategories); 1077 return; 1078 case MOVE_RESULT_OBJECT: 1079 verifyMoveResult(analyzedInstruction, ReferenceCategories); 1080 return; 1081 case MOVE_EXCEPTION: 1082 verifyMoveException(analyzedInstruction); 1083 return; 1084 case RETURN_VOID: 1085 verifyReturnVoid(analyzedInstruction); 1086 return; 1087 case RETURN: 1088 verifyReturn(analyzedInstruction, Primitive32BitCategories); 1089 return; 1090 case RETURN_WIDE: 1091 verifyReturn(analyzedInstruction, WideLowCategories); 1092 return; 1093 case RETURN_OBJECT: 1094 verifyReturn(analyzedInstruction, ReferenceCategories); 1095 return; 1096 case CONST_4: 1097 case CONST_16: 1098 case CONST: 1099 case CONST_HIGH16: 1100 case CONST_WIDE_16: 1101 case CONST_WIDE_32: 1102 case CONST_WIDE: 1103 case CONST_WIDE_HIGH16: 1104 case CONST_STRING: 1105 case CONST_STRING_JUMBO: 1106 return; 1107 case CONST_CLASS: 1108 verifyConstClass(analyzedInstruction); 1109 return; 1110 case MONITOR_ENTER: 1111 case MONITOR_EXIT: 1112 verifyMonitor(analyzedInstruction); 1113 return; 1114 case CHECK_CAST: 1115 verifyCheckCast(analyzedInstruction); 1116 return; 1117 case INSTANCE_OF: 1118 verifyInstanceOf(analyzedInstruction); 1119 return; 1120 case ARRAY_LENGTH: 1121 verifyArrayLength(analyzedInstruction); 1122 return; 1123 case NEW_INSTANCE: 1124 verifyNewInstance(analyzedInstruction); 1125 return; 1126 case NEW_ARRAY: 1127 verifyNewArray(analyzedInstruction); 1128 return; 1129 case FILLED_NEW_ARRAY: 1130 verifyFilledNewArray(analyzedInstruction); 1131 return; 1132 case FILLED_NEW_ARRAY_RANGE: 1133 verifyFilledNewArrayRange(analyzedInstruction); 1134 return; 1135 case FILL_ARRAY_DATA: 1136 verifyFillArrayData(analyzedInstruction); 1137 return; 1138 case THROW: 1139 verifyThrow(analyzedInstruction); 1140 return; 1141 case GOTO: 1142 case GOTO_16: 1143 case GOTO_32: 1144 return; 1145 case PACKED_SWITCH: 1146 verifySwitch(analyzedInstruction, Format.PackedSwitchData); 1147 return; 1148 case SPARSE_SWITCH: 1149 verifySwitch(analyzedInstruction, Format.SparseSwitchData); 1150 return; 1151 case CMPL_FLOAT: 1152 case CMPG_FLOAT: 1153 verifyFloatWideCmp(analyzedInstruction, Primitive32BitCategories); 1154 return; 1155 case CMPL_DOUBLE: 1156 case CMPG_DOUBLE: 1157 case CMP_LONG: 1158 verifyFloatWideCmp(analyzedInstruction, WideLowCategories); 1159 return; 1160 case IF_EQ: 1161 case IF_NE: 1162 verifyIfEqNe(analyzedInstruction); 1163 return; 1164 case IF_LT: 1165 case IF_GE: 1166 case IF_GT: 1167 case IF_LE: 1168 verifyIf(analyzedInstruction); 1169 return; 1170 case IF_EQZ: 1171 case IF_NEZ: 1172 verifyIfEqzNez(analyzedInstruction); 1173 return; 1174 case IF_LTZ: 1175 case IF_GEZ: 1176 case IF_GTZ: 1177 case IF_LEZ: 1178 verifyIfz(analyzedInstruction); 1179 return; 1180 case AGET: 1181 verify32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Integer); 1182 return; 1183 case AGET_BOOLEAN: 1184 verify32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Boolean); 1185 return; 1186 case AGET_BYTE: 1187 verify32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Byte); 1188 return; 1189 case AGET_CHAR: 1190 verify32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Char); 1191 return; 1192 case AGET_SHORT: 1193 verify32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Short); 1194 return; 1195 case AGET_WIDE: 1196 verifyAgetWide(analyzedInstruction); 1197 return; 1198 case AGET_OBJECT: 1199 verifyAgetObject(analyzedInstruction); 1200 return; 1201 case APUT: 1202 verify32BitPrimitiveAput(analyzedInstruction, RegisterType.Category.Integer); 1203 return; 1204 case APUT_BOOLEAN: 1205 verify32BitPrimitiveAput(analyzedInstruction, RegisterType.Category.Boolean); 1206 return; 1207 case APUT_BYTE: 1208 verify32BitPrimitiveAput(analyzedInstruction, RegisterType.Category.Byte); 1209 return; 1210 case APUT_CHAR: 1211 verify32BitPrimitiveAput(analyzedInstruction, RegisterType.Category.Char); 1212 return; 1213 case APUT_SHORT: 1214 verify32BitPrimitiveAput(analyzedInstruction, RegisterType.Category.Short); 1215 return; 1216 case APUT_WIDE: 1217 verifyAputWide(analyzedInstruction); 1218 return; 1219 case APUT_OBJECT: 1220 verifyAputObject(analyzedInstruction); 1221 return; 1222 case IGET: 1223 verify32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Integer); 1224 return; 1225 case IGET_BOOLEAN: 1226 verify32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Boolean); 1227 return; 1228 case IGET_BYTE: 1229 verify32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Byte); 1230 return; 1231 case IGET_CHAR: 1232 verify32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Char); 1233 return; 1234 case IGET_SHORT: 1235 verify32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Short); 1236 return; 1237 case IGET_WIDE: 1238 verifyIgetWide(analyzedInstruction); 1239 return; 1240 case IGET_OBJECT: 1241 verifyIgetObject(analyzedInstruction); 1242 return; 1243 case IPUT: 1244 verify32BitPrimitiveIput(analyzedInstruction, RegisterType.Category.Integer); 1245 return; 1246 case IPUT_BOOLEAN: 1247 verify32BitPrimitiveIput(analyzedInstruction, RegisterType.Category.Boolean); 1248 return; 1249 case IPUT_BYTE: 1250 verify32BitPrimitiveIput(analyzedInstruction, RegisterType.Category.Byte); 1251 return; 1252 case IPUT_CHAR: 1253 verify32BitPrimitiveIput(analyzedInstruction, RegisterType.Category.Char); 1254 return; 1255 case IPUT_SHORT: 1256 verify32BitPrimitiveIput(analyzedInstruction, RegisterType.Category.Short); 1257 return; 1258 case IPUT_WIDE: 1259 verifyIputWide(analyzedInstruction); 1260 return; 1261 case IPUT_OBJECT: 1262 verifyIputObject(analyzedInstruction); 1263 return; 1264 case SGET: 1265 verify32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Integer); 1266 return; 1267 case SGET_BOOLEAN: 1268 verify32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Boolean); 1269 return; 1270 case SGET_BYTE: 1271 verify32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Byte); 1272 return; 1273 case SGET_CHAR: 1274 verify32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Char); 1275 return; 1276 case SGET_SHORT: 1277 verify32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Short); 1278 return; 1279 case SGET_WIDE: 1280 verifySgetWide(analyzedInstruction); 1281 return; 1282 case SGET_OBJECT: 1283 verifySgetObject(analyzedInstruction); 1284 return; 1285 case SPUT: 1286 verify32BitPrimitiveSput(analyzedInstruction, RegisterType.Category.Integer); 1287 return; 1288 case SPUT_BOOLEAN: 1289 verify32BitPrimitiveSput(analyzedInstruction, RegisterType.Category.Boolean); 1290 return; 1291 case SPUT_BYTE: 1292 verify32BitPrimitiveSput(analyzedInstruction, RegisterType.Category.Byte); 1293 return; 1294 case SPUT_CHAR: 1295 verify32BitPrimitiveSput(analyzedInstruction, RegisterType.Category.Char); 1296 return; 1297 case SPUT_SHORT: 1298 verify32BitPrimitiveSput(analyzedInstruction, RegisterType.Category.Short); 1299 return; 1300 case SPUT_WIDE: 1301 verifySputWide(analyzedInstruction); 1302 return; 1303 case SPUT_OBJECT: 1304 verifySputObject(analyzedInstruction); 1305 return; 1306 case INVOKE_VIRTUAL: 1307 verifyInvoke(analyzedInstruction, INVOKE_VIRTUAL); 1308 return; 1309 case INVOKE_SUPER: 1310 verifyInvoke(analyzedInstruction, INVOKE_SUPER); 1311 return; 1312 case INVOKE_DIRECT: 1313 verifyInvoke(analyzedInstruction, INVOKE_DIRECT); 1314 return; 1315 case INVOKE_STATIC: 1316 verifyInvoke(analyzedInstruction, INVOKE_STATIC); 1317 return; 1318 case INVOKE_INTERFACE: 1319 verifyInvoke(analyzedInstruction, INVOKE_INTERFACE); 1320 return; 1321 case INVOKE_VIRTUAL_RANGE: 1322 verifyInvokeRange(analyzedInstruction, INVOKE_VIRTUAL); 1323 return; 1324 case INVOKE_SUPER_RANGE: 1325 verifyInvokeRange(analyzedInstruction, INVOKE_SUPER); 1326 return; 1327 case INVOKE_DIRECT_RANGE: 1328 verifyInvokeRange(analyzedInstruction, INVOKE_DIRECT); 1329 return; 1330 case INVOKE_STATIC_RANGE: 1331 verifyInvokeRange(analyzedInstruction, INVOKE_STATIC); 1332 return; 1333 case INVOKE_INTERFACE_RANGE: 1334 verifyInvokeRange(analyzedInstruction, INVOKE_INTERFACE); 1335 return; 1336 case NEG_INT: 1337 case NOT_INT: 1338 verifyUnaryOp(analyzedInstruction, Primitive32BitCategories); 1339 return; 1340 case NEG_LONG: 1341 case NOT_LONG: 1342 verifyUnaryOp(analyzedInstruction, WideLowCategories); 1343 return; 1344 case NEG_FLOAT: 1345 verifyUnaryOp(analyzedInstruction, Primitive32BitCategories); 1346 return; 1347 case NEG_DOUBLE: 1348 verifyUnaryOp(analyzedInstruction, WideLowCategories); 1349 return; 1350 case INT_TO_LONG: 1351 verifyUnaryOp(analyzedInstruction, Primitive32BitCategories); 1352 return; 1353 case INT_TO_FLOAT: 1354 verifyUnaryOp(analyzedInstruction, Primitive32BitCategories); 1355 return; 1356 case INT_TO_DOUBLE: 1357 verifyUnaryOp(analyzedInstruction, Primitive32BitCategories); 1358 return; 1359 case LONG_TO_INT: 1360 case DOUBLE_TO_INT: 1361 verifyUnaryOp(analyzedInstruction, WideLowCategories); 1362 return; 1363 case LONG_TO_FLOAT: 1364 case DOUBLE_TO_FLOAT: 1365 verifyUnaryOp(analyzedInstruction, WideLowCategories); 1366 return; 1367 case LONG_TO_DOUBLE: 1368 verifyUnaryOp(analyzedInstruction, WideLowCategories); 1369 return; 1370 case FLOAT_TO_INT: 1371 verifyUnaryOp(analyzedInstruction, Primitive32BitCategories); 1372 return; 1373 case FLOAT_TO_LONG: 1374 verifyUnaryOp(analyzedInstruction, Primitive32BitCategories); 1375 return; 1376 case FLOAT_TO_DOUBLE: 1377 verifyUnaryOp(analyzedInstruction, Primitive32BitCategories); 1378 return; 1379 case DOUBLE_TO_LONG: 1380 verifyUnaryOp(analyzedInstruction, WideLowCategories); 1381 return; 1382 case INT_TO_BYTE: 1383 verifyUnaryOp(analyzedInstruction, Primitive32BitCategories); 1384 return; 1385 case INT_TO_CHAR: 1386 verifyUnaryOp(analyzedInstruction, Primitive32BitCategories); 1387 return; 1388 case INT_TO_SHORT: 1389 verifyUnaryOp(analyzedInstruction, Primitive32BitCategories); 1390 return; 1391 case ADD_INT: 1392 case SUB_INT: 1393 case MUL_INT: 1394 case DIV_INT: 1395 case REM_INT: 1396 case SHL_INT: 1397 case SHR_INT: 1398 case USHR_INT: 1399 case AND_INT: 1400 case OR_INT: 1401 case XOR_INT: 1402 verifyBinaryOp(analyzedInstruction, Primitive32BitCategories, Primitive32BitCategories); 1403 return; 1404 case ADD_LONG: 1405 case SUB_LONG: 1406 case MUL_LONG: 1407 case DIV_LONG: 1408 case REM_LONG: 1409 case AND_LONG: 1410 case OR_LONG: 1411 case XOR_LONG: 1412 verifyBinaryOp(analyzedInstruction, WideLowCategories, WideLowCategories); 1413 return; 1414 case SHL_LONG: 1415 case SHR_LONG: 1416 case USHR_LONG: 1417 verifyBinaryOp(analyzedInstruction, WideLowCategories, Primitive32BitCategories); 1418 return; 1419 case ADD_FLOAT: 1420 case SUB_FLOAT: 1421 case MUL_FLOAT: 1422 case DIV_FLOAT: 1423 case REM_FLOAT: 1424 verifyBinaryOp(analyzedInstruction, Primitive32BitCategories, Primitive32BitCategories); 1425 return; 1426 case ADD_DOUBLE: 1427 case SUB_DOUBLE: 1428 case MUL_DOUBLE: 1429 case DIV_DOUBLE: 1430 case REM_DOUBLE: 1431 verifyBinaryOp(analyzedInstruction, WideLowCategories, WideLowCategories); 1432 return; 1433 case ADD_INT_2ADDR: 1434 case SUB_INT_2ADDR: 1435 case MUL_INT_2ADDR: 1436 case DIV_INT_2ADDR: 1437 case REM_INT_2ADDR: 1438 case SHL_INT_2ADDR: 1439 case SHR_INT_2ADDR: 1440 case USHR_INT_2ADDR: 1441 case AND_INT_2ADDR: 1442 case OR_INT_2ADDR: 1443 case XOR_INT_2ADDR: 1444 verifyBinary2AddrOp(analyzedInstruction, Primitive32BitCategories, Primitive32BitCategories); 1445 return; 1446 case ADD_LONG_2ADDR: 1447 case SUB_LONG_2ADDR: 1448 case MUL_LONG_2ADDR: 1449 case DIV_LONG_2ADDR: 1450 case REM_LONG_2ADDR: 1451 case AND_LONG_2ADDR: 1452 case OR_LONG_2ADDR: 1453 case XOR_LONG_2ADDR: 1454 verifyBinary2AddrOp(analyzedInstruction, WideLowCategories, WideLowCategories); 1455 return; 1456 case SHL_LONG_2ADDR: 1457 case SHR_LONG_2ADDR: 1458 case USHR_LONG_2ADDR: 1459 verifyBinary2AddrOp(analyzedInstruction, WideLowCategories, Primitive32BitCategories); 1460 return; 1461 case ADD_FLOAT_2ADDR: 1462 case SUB_FLOAT_2ADDR: 1463 case MUL_FLOAT_2ADDR: 1464 case DIV_FLOAT_2ADDR: 1465 case REM_FLOAT_2ADDR: 1466 verifyBinary2AddrOp(analyzedInstruction, Primitive32BitCategories, Primitive32BitCategories); 1467 return; 1468 case ADD_DOUBLE_2ADDR: 1469 case SUB_DOUBLE_2ADDR: 1470 case MUL_DOUBLE_2ADDR: 1471 case DIV_DOUBLE_2ADDR: 1472 case REM_DOUBLE_2ADDR: 1473 verifyBinary2AddrOp(analyzedInstruction, WideLowCategories, WideLowCategories); 1474 return; 1475 case ADD_INT_LIT16: 1476 case RSUB_INT: 1477 case MUL_INT_LIT16: 1478 case DIV_INT_LIT16: 1479 case REM_INT_LIT16: 1480 verifyLiteralBinaryOp(analyzedInstruction); 1481 return; 1482 case AND_INT_LIT16: 1483 case OR_INT_LIT16: 1484 case XOR_INT_LIT16: 1485 verifyLiteralBinaryOp(analyzedInstruction); 1486 return; 1487 case ADD_INT_LIT8: 1488 case RSUB_INT_LIT8: 1489 case MUL_INT_LIT8: 1490 case DIV_INT_LIT8: 1491 case REM_INT_LIT8: 1492 case SHL_INT_LIT8: 1493 verifyLiteralBinaryOp(analyzedInstruction); 1494 return; 1495 case AND_INT_LIT8: 1496 case OR_INT_LIT8: 1497 case XOR_INT_LIT8: 1498 verifyLiteralBinaryOp(analyzedInstruction); 1499 return; 1500 case SHR_INT_LIT8: 1501 verifyLiteralBinaryOp(analyzedInstruction); 1502 return; 1503 case USHR_INT_LIT8: 1504 verifyLiteralBinaryOp(analyzedInstruction); 1505 return; 1506 case IGET_VOLATILE: 1507 case IPUT_VOLATILE: 1508 case SGET_VOLATILE: 1509 case SPUT_VOLATILE: 1510 case IGET_OBJECT_VOLATILE: 1511 case IGET_WIDE_VOLATILE: 1512 case IPUT_WIDE_VOLATILE: 1513 case SGET_WIDE_VOLATILE: 1514 case SPUT_WIDE_VOLATILE: 1515 case EXECUTE_INLINE: 1516 case EXECUTE_INLINE_RANGE: 1517 case INVOKE_DIRECT_EMPTY: 1518 case IGET_QUICK: 1519 case IGET_WIDE_QUICK: 1520 case IGET_OBJECT_QUICK: 1521 case IPUT_QUICK: 1522 case IPUT_WIDE_QUICK: 1523 case IPUT_OBJECT_QUICK: 1524 case INVOKE_VIRTUAL_QUICK: 1525 case INVOKE_SUPER_QUICK: 1526 case INVOKE_VIRTUAL_QUICK_RANGE: 1527 case INVOKE_SUPER_QUICK_RANGE: 1528 case IPUT_OBJECT_VOLATILE: 1529 case SGET_OBJECT_VOLATILE: 1530 case SPUT_OBJECT_VOLATILE: 1531 //TODO: throw validation exception? 1532 default: 1533 assert false; 1534 return; 1535 } 1536 } 1537 1538 private static final EnumSet<RegisterType.Category> Primitive32BitCategories = EnumSet.of( 1539 RegisterType.Category.Null, 1540 RegisterType.Category.One, 1541 RegisterType.Category.Boolean, 1542 RegisterType.Category.Byte, 1543 RegisterType.Category.PosByte, 1544 RegisterType.Category.Short, 1545 RegisterType.Category.PosShort, 1546 RegisterType.Category.Char, 1547 RegisterType.Category.Integer, 1548 RegisterType.Category.Float); 1549 1550 private static final EnumSet<RegisterType.Category> WideLowCategories = EnumSet.of( 1551 RegisterType.Category.LongLo, 1552 RegisterType.Category.DoubleLo); 1553 1554 private static final EnumSet<RegisterType.Category> WideHighCategories = EnumSet.of( 1555 RegisterType.Category.LongHi, 1556 RegisterType.Category.DoubleHi); 1557 1558 private static final EnumSet<RegisterType.Category> ReferenceCategories = EnumSet.of( 1559 RegisterType.Category.Null, 1560 RegisterType.Category.Reference); 1561 1562 private static final EnumSet<RegisterType.Category> ReferenceOrUninitThisCategories = EnumSet.of( 1563 RegisterType.Category.Null, 1564 RegisterType.Category.UninitThis, 1565 RegisterType.Category.Reference); 1566 1567 private static final EnumSet<RegisterType.Category> ReferenceOrUninitCategories = EnumSet.of( 1568 RegisterType.Category.Null, 1569 RegisterType.Category.UninitRef, 1570 RegisterType.Category.UninitThis, 1571 RegisterType.Category.Reference); 1572 1573 private static final EnumSet<RegisterType.Category> ReferenceAndPrimitive32BitCategories = EnumSet.of( 1574 RegisterType.Category.Null, 1575 RegisterType.Category.One, 1576 RegisterType.Category.Boolean, 1577 RegisterType.Category.Byte, 1578 RegisterType.Category.PosByte, 1579 RegisterType.Category.Short, 1580 RegisterType.Category.PosShort, 1581 RegisterType.Category.Char, 1582 RegisterType.Category.Integer, 1583 RegisterType.Category.Float, 1584 RegisterType.Category.Reference); 1585 1586 private static final EnumSet<RegisterType.Category> BooleanCategories = EnumSet.of( 1587 RegisterType.Category.Null, 1588 RegisterType.Category.One, 1589 RegisterType.Category.Boolean); 1590 1591 private void analyzeMove(AnalyzedInstruction analyzedInstruction) { 1592 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; 1593 1594 RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); 1595 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, sourceRegisterType); 1596 } 1597 1598 private void verifyMove(AnalyzedInstruction analyzedInstruction, EnumSet validCategories) { 1599 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; 1600 1601 getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), validCategories); 1602 } 1603 1604 private void analyzeMoveResult(AnalyzedInstruction analyzedInstruction) { 1605 AnalyzedInstruction previousInstruction = instructions.valueAt(analyzedInstruction.instructionIndex-1); 1606 if (!previousInstruction.instruction.opcode.setsResult()) { 1607 throw new ValidationException(analyzedInstruction.instruction.opcode.name + " must occur after an " + 1608 "invoke-*/fill-new-array instruction"); 1609 } 1610 1611 RegisterType resultRegisterType; 1612 InstructionWithReference invokeInstruction = (InstructionWithReference)previousInstruction.instruction; 1613 Item item = invokeInstruction.getReferencedItem(); 1614 1615 if (item.getItemType() == ItemType.TYPE_METHOD_ID_ITEM) { 1616 resultRegisterType = RegisterType.getRegisterTypeForTypeIdItem( 1617 ((MethodIdItem)item).getPrototype().getReturnType()); 1618 } else { 1619 assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM; 1620 resultRegisterType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item); 1621 } 1622 1623 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, resultRegisterType); 1624 } 1625 1626 private void verifyMoveResult(AnalyzedInstruction analyzedInstruction, 1627 EnumSet<RegisterType.Category> allowedCategories) { 1628 if (analyzedInstruction.instructionIndex == 0) { 1629 throw new ValidationException(analyzedInstruction.instruction.opcode.name + " cannot be the first " + 1630 "instruction in a method. It must occur after an invoke-*/fill-new-array instruction"); 1631 } 1632 1633 AnalyzedInstruction previousInstruction = instructions.valueAt(analyzedInstruction.instructionIndex-1); 1634 1635 if (!previousInstruction.instruction.opcode.setsResult()) { 1636 throw new ValidationException(analyzedInstruction.instruction.opcode.name + " must occur after an " + 1637 "invoke-*/fill-new-array instruction"); 1638 } 1639 1640 //TODO: does dalvik allow a move-result after an invoke with a void return type? 1641 RegisterType resultRegisterType; 1642 1643 InstructionWithReference invokeInstruction = (InstructionWithReference)previousInstruction.getInstruction(); 1644 Item item = invokeInstruction.getReferencedItem(); 1645 1646 if (item instanceof MethodIdItem) { 1647 resultRegisterType = RegisterType.getRegisterTypeForTypeIdItem( 1648 ((MethodIdItem)item).getPrototype().getReturnType()); 1649 } else { 1650 assert item instanceof TypeIdItem; 1651 resultRegisterType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item); 1652 } 1653 1654 if (!allowedCategories.contains(resultRegisterType.category)) { 1655 throw new ValidationException(String.format("Wrong move-result* instruction for return value %s", 1656 resultRegisterType.toString())); 1657 } 1658 } 1659 1660 private void analyzeMoveException(AnalyzedInstruction analyzedInstruction) { 1661 CodeItem.TryItem[] tries = encodedMethod.codeItem.getTries(); 1662 int instructionAddress = getInstructionAddress(analyzedInstruction); 1663 1664 if (tries == null) { 1665 throw new ValidationException("move-exception must be the first instruction in an exception handler block"); 1666 } 1667 1668 RegisterType exceptionType = null; 1669 1670 for (CodeItem.TryItem tryItem: encodedMethod.codeItem.getTries()) { 1671 if (tryItem.encodedCatchHandler.getCatchAllHandlerAddress() == instructionAddress) { 1672 exceptionType = RegisterType.getRegisterType(RegisterType.Category.Reference, 1673 ClassPath.getClassDef("Ljava/lang/Throwable;")); 1674 break; 1675 } 1676 for (CodeItem.EncodedTypeAddrPair handler: tryItem.encodedCatchHandler.handlers) { 1677 if (handler.getHandlerAddress() == instructionAddress) { 1678 exceptionType = RegisterType.getRegisterTypeForTypeIdItem(handler.exceptionType) 1679 .merge(exceptionType); 1680 } 1681 } 1682 } 1683 1684 if (exceptionType == null) { 1685 throw new ValidationException("move-exception must be the first instruction in an exception handler block"); 1686 } 1687 1688 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, exceptionType); 1689 } 1690 1691 private void verifyMoveException(AnalyzedInstruction analyzedInstruction) { 1692 CodeItem.TryItem[] tries = encodedMethod.codeItem.getTries(); 1693 int instructionAddress = getInstructionAddress(analyzedInstruction); 1694 1695 if (tries == null) { 1696 throw new ValidationException("move-exception must be the first instruction in an exception handler block"); 1697 } 1698 1699 RegisterType exceptionType = null; 1700 1701 for (CodeItem.TryItem tryItem: encodedMethod.codeItem.getTries()) { 1702 if (tryItem.encodedCatchHandler.getCatchAllHandlerAddress() == instructionAddress) { 1703 exceptionType = RegisterType.getRegisterType(RegisterType.Category.Reference, 1704 ClassPath.getClassDef("Ljava/lang/Throwable;")); 1705 break; 1706 } 1707 for (CodeItem.EncodedTypeAddrPair handler: tryItem.encodedCatchHandler.handlers) { 1708 if (handler.getHandlerAddress() == instructionAddress) { 1709 exceptionType = RegisterType.getRegisterTypeForTypeIdItem(handler.exceptionType) 1710 .merge(exceptionType); 1711 } 1712 } 1713 } 1714 1715 if (exceptionType == null) { 1716 throw new ValidationException("move-exception must be the first instruction in an exception handler block"); 1717 } 1718 1719 //TODO: check if the type is a throwable. Should we throw a ValidationException or print a warning? (does dalvik validate that it's a throwable? It doesn't in CodeVerify.c, but it might check in DexSwapVerify.c) 1720 if (exceptionType.category != RegisterType.Category.Reference) { 1721 throw new ValidationException(String.format("Exception type %s is not a reference type", 1722 exceptionType.toString())); 1723 } 1724 } 1725 1726 private void verifyReturnVoid(AnalyzedInstruction analyzedInstruction) { 1727 TypeIdItem returnType = encodedMethod.method.getPrototype().getReturnType(); 1728 if (returnType.getTypeDescriptor().charAt(0) != 'V') { 1729 //TODO: could add which return-* variation should be used instead 1730 throw new ValidationException("Cannot use return-void with a non-void return type (" + 1731 returnType.getTypeDescriptor() + ")"); 1732 } 1733 } 1734 1735 private void verifyReturn(AnalyzedInstruction analyzedInstruction, EnumSet validCategories) { 1736 /*if (this.isInstanceConstructor()) { 1737 checkConstructorReturn(analyzedInstruction); 1738 }*/ 1739 1740 SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; 1741 int returnRegister = instruction.getRegisterA(); 1742 RegisterType returnRegisterType = getAndCheckSourceRegister(analyzedInstruction, returnRegister, 1743 validCategories); 1744 1745 TypeIdItem returnType = encodedMethod.method.getPrototype().getReturnType(); 1746 if (returnType.getTypeDescriptor().charAt(0) == 'V') { 1747 throw new ValidationException("Cannot use return with a void return type. Use return-void instead"); 1748 } 1749 1750 RegisterType methodReturnRegisterType = RegisterType.getRegisterTypeForTypeIdItem(returnType); 1751 1752 if (!validCategories.contains(methodReturnRegisterType.category)) { 1753 //TODO: could add which return-* variation should be used instead 1754 throw new ValidationException(String.format("Cannot use %s with return type %s", 1755 analyzedInstruction.instruction.opcode.name, returnType.getTypeDescriptor())); 1756 } 1757 1758 if (validCategories == ReferenceCategories) { 1759 if (methodReturnRegisterType.type.isInterface()) { 1760 if (returnRegisterType.category != RegisterType.Category.Null && 1761 !returnRegisterType.type.implementsInterface(methodReturnRegisterType.type)) { 1762 //TODO: how to handle warnings? 1763 } 1764 } else { 1765 if (returnRegisterType.category == RegisterType.Category.Reference && 1766 !returnRegisterType.type.extendsClass(methodReturnRegisterType.type)) { 1767 1768 throw new ValidationException(String.format("The return value in register v%d (%s) is not " + 1769 "compatible with the method's return type %s", returnRegister, 1770 returnRegisterType.type.getClassType(), methodReturnRegisterType.type.getClassType())); 1771 } 1772 } 1773 } 1774 } 1775 1776 private void analyzeConst(AnalyzedInstruction analyzedInstruction) { 1777 LiteralInstruction instruction = (LiteralInstruction)analyzedInstruction.instruction; 1778 1779 RegisterType newDestinationRegisterType = RegisterType.getRegisterTypeForLiteral(instruction.getLiteral()); 1780 1781 //we assume that the literal value is a valid value for the given instruction type, because it's impossible 1782 //to store an invalid literal with the instruction. so we don't need to check the type of the literal 1783 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, newDestinationRegisterType); 1784 } 1785 1786 private void analyzeConstHigh16(AnalyzedInstruction analyzedInstruction) { 1787 //the literal value stored in the instruction is a 16-bit value. When shifted left by 16, it will always be an 1788 //integer 1789 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 1790 RegisterType.getRegisterType(RegisterType.Category.Integer, null)); 1791 } 1792 1793 private void analyzeWideConst(AnalyzedInstruction analyzedInstruction) { 1794 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 1795 RegisterType.getRegisterType(RegisterType.Category.LongLo, null)); 1796 } 1797 1798 private void analyzeConstString(AnalyzedInstruction analyzedInstruction) { 1799 ClassPath.ClassDef stringClassDef = ClassPath.getClassDef("Ljava/lang/String;"); 1800 RegisterType stringType = RegisterType.getRegisterType(RegisterType.Category.Reference, stringClassDef); 1801 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, stringType); 1802 } 1803 1804 private void analyzeConstClass(AnalyzedInstruction analyzedInstruction) { 1805 ClassPath.ClassDef classClassDef = ClassPath.getClassDef("Ljava/lang/Class;"); 1806 RegisterType classType = RegisterType.getRegisterType(RegisterType.Category.Reference, classClassDef); 1807 1808 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, classType); 1809 } 1810 1811 1812 private void verifyConstClass(AnalyzedInstruction analyzedInstruction) { 1813 ClassPath.ClassDef classClassDef = ClassPath.getClassDef("Ljava/lang/Class;"); 1814 RegisterType classType = RegisterType.getRegisterType(RegisterType.Category.Reference, classClassDef); 1815 1816 InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; 1817 Item item = instruction.getReferencedItem(); 1818 assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM; 1819 1820 //TODO: need to check class access 1821 //make sure the referenced class is resolvable 1822 ClassPath.getClassDef((TypeIdItem)item); 1823 } 1824 1825 private void verifyMonitor(AnalyzedInstruction analyzedInstruction) { 1826 SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; 1827 getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), ReferenceCategories); 1828 } 1829 1830 private void analyzeCheckCast(AnalyzedInstruction analyzedInstruction) { 1831 InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; 1832 1833 Item item = instruction.getReferencedItem(); 1834 assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM; 1835 1836 RegisterType castRegisterType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item); 1837 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, castRegisterType); 1838 } 1839 1840 private void verifyCheckCast(AnalyzedInstruction analyzedInstruction) { 1841 { 1842 //ensure the "source" register is a reference type 1843 SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; 1844 1845 RegisterType registerType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), 1846 ReferenceCategories); 1847 } 1848 1849 { 1850 //resolve and verify the class that we're casting to 1851 InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; 1852 1853 Item item = instruction.getReferencedItem(); 1854 assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM; 1855 1856 //TODO: need to check class access 1857 RegisterType castRegisterType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item); 1858 if (castRegisterType.category != RegisterType.Category.Reference) { 1859 //TODO: verify that dalvik allows a non-reference type.. 1860 //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) 1861 } 1862 } 1863 } 1864 1865 private void analyzeInstanceOf(AnalyzedInstruction analyzedInstruction) { 1866 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 1867 RegisterType.getRegisterType(RegisterType.Category.Boolean, null)); 1868 } 1869 1870 private void verifyInstanceOf(AnalyzedInstruction analyzedInstruction) { 1871 { 1872 //ensure the register that is being checks is a reference type 1873 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; 1874 1875 getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), ReferenceCategories); 1876 } 1877 1878 { 1879 //resolve and verify the class that we're checking against 1880 InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; 1881 1882 Item item = instruction.getReferencedItem(); 1883 assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM; 1884 RegisterType registerType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item); 1885 if (registerType.category != RegisterType.Category.Reference) { 1886 throw new ValidationException(String.format("Cannot use instance-of with a non-reference type %s", 1887 registerType.toString())); 1888 } 1889 1890 //TODO: is it valid to use an array type? 1891 //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. 1892 } 1893 } 1894 1895 private void analyzeArrayLength(AnalyzedInstruction analyzedInstruction) { 1896 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 1897 RegisterType.getRegisterType(RegisterType.Category.Integer, null)); 1898 } 1899 1900 private void verifyArrayLength(AnalyzedInstruction analyzedInstruction) { 1901 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; 1902 1903 int arrayRegisterNumber = instruction.getRegisterB(); 1904 RegisterType arrayRegisterType = getAndCheckSourceRegister(analyzedInstruction, arrayRegisterNumber, 1905 ReferenceCategories); 1906 1907 if (arrayRegisterType.type != null) { 1908 if (arrayRegisterType.type.getClassType().charAt(0) != '[') { 1909 throw new ValidationException(String.format("Cannot use array-length with non-array type %s", 1910 arrayRegisterType.type.getClassType())); 1911 } 1912 assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef; 1913 } 1914 } 1915 1916 private void analyzeNewInstance(AnalyzedInstruction analyzedInstruction) { 1917 InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; 1918 1919 int register = ((SingleRegisterInstruction)analyzedInstruction.instruction).getRegisterA(); 1920 RegisterType destRegisterType = analyzedInstruction.getPostInstructionRegisterType(register); 1921 if (destRegisterType.category != RegisterType.Category.Unknown) { 1922 assert destRegisterType.category == RegisterType.Category.UninitRef; 1923 1924 //the post-instruction destination register will only be set if we have already analyzed this instruction 1925 //at least once. If this is the case, then the uninit reference has already been propagated to all 1926 //successors and nothing else needs to be done. 1927 return; 1928 } 1929 1930 Item item = instruction.getReferencedItem(); 1931 assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM; 1932 1933 RegisterType classType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item); 1934 1935 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 1936 RegisterType.getUnitializedReference(classType.type)); 1937 } 1938 1939 private void verifyNewInstance(AnalyzedInstruction analyzedInstruction) { 1940 InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; 1941 1942 int register = ((SingleRegisterInstruction)analyzedInstruction.instruction).getRegisterA(); 1943 RegisterType destRegisterType = analyzedInstruction.postRegisterMap[register]; 1944 if (destRegisterType.category != RegisterType.Category.Unknown) { 1945 assert destRegisterType.category == RegisterType.Category.UninitRef; 1946 1947 //the "post-instruction" destination register will only be set if we've gone over 1948 //this instruction at least once before. If this is the case, then we need to check 1949 //all the other registers, and make sure that none of them contain the same 1950 //uninitialized reference that is in the destination register. 1951 1952 for (int i=0; i<analyzedInstruction.postRegisterMap.length; i++) { 1953 if (i==register) { 1954 continue; 1955 } 1956 1957 if (analyzedInstruction.getPreInstructionRegisterType(i) == destRegisterType) { 1958 throw new ValidationException(String.format("Register v%d contains an uninitialized reference " + 1959 "that was created by this new-instance instruction.", i)); 1960 } 1961 } 1962 1963 return; 1964 } 1965 1966 Item item = instruction.getReferencedItem(); 1967 assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM; 1968 1969 //TODO: need to check class access 1970 RegisterType classType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item); 1971 if (classType.category != RegisterType.Category.Reference) { 1972 throw new ValidationException(String.format("Cannot use new-instance with a non-reference type %s", 1973 classType.toString())); 1974 } 1975 1976 if (((TypeIdItem)item).getTypeDescriptor().charAt(0) == '[') { 1977 throw new ValidationException("Cannot use array type \"" + ((TypeIdItem)item).getTypeDescriptor() + 1978 "\" with new-instance. Use new-array instead."); 1979 } 1980 } 1981 1982 private void analyzeNewArray(AnalyzedInstruction analyzedInstruction) { 1983 InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; 1984 1985 Item item = instruction.getReferencedItem(); 1986 assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM; 1987 1988 RegisterType arrayType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item); 1989 assert arrayType.type instanceof ClassPath.ArrayClassDef; 1990 1991 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, arrayType); 1992 } 1993 1994 private void verifyNewArray(AnalyzedInstruction analyzedInstruction) { 1995 { 1996 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; 1997 getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), Primitive32BitCategories); 1998 } 1999 2000 InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; 2001 2002 Item item = instruction.getReferencedItem(); 2003 assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM; 2004 2005 RegisterType arrayType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item); 2006 assert arrayType.type instanceof ClassPath.ArrayClassDef; 2007 2008 if (arrayType.category != RegisterType.Category.Reference) { 2009 throw new ValidationException(String.format("Cannot use new-array with a non-reference type %s", 2010 arrayType.toString())); 2011 } 2012 if (arrayType.type.getClassType().charAt(0) != '[') { 2013 throw new ValidationException("Cannot use non-array type \"" + arrayType.type.getClassType() + 2014 "\" with new-array. Use new-instance instead."); 2015 } 2016 } 2017 2018 private void verifyFilledNewArrayCommon(AnalyzedInstruction analyzedInstruction, 2019 RegisterIterator registerIterator) { 2020 InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; 2021 2022 RegisterType arrayType; 2023 RegisterType arrayImmediateElementType; 2024 2025 Item item = instruction.getReferencedItem(); 2026 assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM; 2027 2028 ClassPath.ClassDef classDef = ClassPath.getClassDef((TypeIdItem)item); 2029 2030 if (classDef.getClassType().charAt(0) != '[') { 2031 throw new ValidationException("Cannot use non-array type \"" + classDef.getClassType() + 2032 "\" with new-array. Use new-instance instead."); 2033 } 2034 2035 ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)classDef; 2036 arrayType = RegisterType.getRegisterType(RegisterType.Category.Reference, classDef); 2037 arrayImmediateElementType = RegisterType.getRegisterTypeForType( 2038 arrayClassDef.getImmediateElementClass().getClassType()); 2039 String baseElementType = arrayClassDef.getBaseElementClass().getClassType(); 2040 if (baseElementType.charAt(0) == 'J' || baseElementType.charAt(0) == 'D') { 2041 throw new ValidationException("Cannot use filled-new-array to create an array of wide values " + 2042 "(long or double)"); 2043 } 2044 2045 do { 2046 int register = registerIterator.getRegister(); 2047 RegisterType elementType = analyzedInstruction.getPreInstructionRegisterType(register); 2048 assert elementType != null; 2049 2050 if (!elementType.canBeAssignedTo(arrayImmediateElementType)) { 2051 throw new ValidationException("Register v" + Integer.toString(register) + " is of type " + 2052 elementType.toString() + " and is incompatible with the array type " + 2053 arrayType.type.getClassType()); 2054 } 2055 } while (registerIterator.moveNext()); 2056 } 2057 2058 private void verifyFilledNewArray(AnalyzedInstruction analyzedInstruction) { 2059 FiveRegisterInstruction instruction = (FiveRegisterInstruction)analyzedInstruction.instruction; 2060 verifyFilledNewArrayCommon(analyzedInstruction, new Format35cRegisterIterator(instruction)); 2061 } 2062 2063 private void verifyFilledNewArrayRange(AnalyzedInstruction analyzedInstruction) { 2064 RegisterRangeInstruction instruction = (RegisterRangeInstruction)analyzedInstruction.instruction; 2065 2066 //instruction.getStartRegister() and instruction.getRegCount() both return an int value, but are actually 2067 //unsigned 16 bit values, so we don't have to worry about overflowing an int when adding them together 2068 if (instruction.getStartRegister() + instruction.getRegCount() >= 1<<16) { 2069 throw new ValidationException(String.format("Invalid register range {v%d .. v%d}. The ending register " + 2070 "is larger than the largest allowed register of v65535.", 2071 instruction.getStartRegister(), 2072 instruction.getStartRegister() + instruction.getRegCount() - 1)); 2073 } 2074 2075 verifyFilledNewArrayCommon(analyzedInstruction, new Format3rcRegisterIterator(instruction)); 2076 } 2077 2078 private void verifyFillArrayData(AnalyzedInstruction analyzedInstruction) { 2079 SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; 2080 2081 int register = instruction.getRegisterA(); 2082 RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(register); 2083 assert registerType != null; 2084 2085 if (registerType.category == RegisterType.Category.Null) { 2086 return; 2087 } 2088 2089 if (registerType.category != RegisterType.Category.Reference) { 2090 throw new ValidationException(String.format("Cannot use fill-array-data with non-array register v%d of " + 2091 "type %s", register, registerType.toString())); 2092 } 2093 2094 assert registerType.type instanceof ClassPath.ArrayClassDef; 2095 ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)registerType.type; 2096 2097 if (arrayClassDef.getArrayDimensions() != 1) { 2098 throw new ValidationException(String.format("Cannot use fill-array-data with array type %s. It can only " + 2099 "be used with a one-dimensional array of primitives.", arrayClassDef.getClassType())); 2100 } 2101 2102 int elementWidth; 2103 switch (arrayClassDef.getBaseElementClass().getClassType().charAt(0)) { 2104 case 'Z': 2105 case 'B': 2106 elementWidth = 1; 2107 break; 2108 case 'C': 2109 case 'S': 2110 elementWidth = 2; 2111 break; 2112 case 'I': 2113 case 'F': 2114 elementWidth = 4; 2115 break; 2116 case 'J': 2117 case 'D': 2118 elementWidth = 8; 2119 break; 2120 default: 2121 throw new ValidationException(String.format("Cannot use fill-array-data with array type %s. It can " + 2122 "only be used with a one-dimensional array of primitives.", arrayClassDef.getClassType())); 2123 } 2124 2125 2126 int arrayDataAddressOffset = ((OffsetInstruction)analyzedInstruction.instruction).getTargetAddressOffset(); 2127 int arrayDataCodeAddress = getInstructionAddress(analyzedInstruction) + arrayDataAddressOffset; 2128 AnalyzedInstruction arrayDataInstruction = this.instructions.get(arrayDataCodeAddress); 2129 if (arrayDataInstruction == null || arrayDataInstruction.instruction.getFormat() != Format.ArrayData) { 2130 throw new ValidationException(String.format("Could not find an array data structure at code address 0x%x", 2131 arrayDataCodeAddress)); 2132 } 2133 2134 ArrayDataPseudoInstruction arrayDataPseudoInstruction = 2135 (ArrayDataPseudoInstruction)arrayDataInstruction.instruction; 2136 2137 if (elementWidth != arrayDataPseudoInstruction.getElementWidth()) { 2138 throw new ValidationException(String.format("The array data at code address 0x%x does not have the " + 2139 "correct element width for array type %s. Expecting element width %d, got element width %d.", 2140 arrayDataCodeAddress, arrayClassDef.getClassType(), elementWidth, 2141 arrayDataPseudoInstruction.getElementWidth())); 2142 } 2143 } 2144 2145 private void verifyThrow(AnalyzedInstruction analyzedInstruction) { 2146 int register = ((SingleRegisterInstruction)analyzedInstruction.instruction).getRegisterA(); 2147 2148 RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(register); 2149 assert registerType != null; 2150 2151 if (registerType.category == RegisterType.Category.Null) { 2152 return; 2153 } 2154 2155 if (registerType.category != RegisterType.Category.Reference) { 2156 throw new ValidationException(String.format("Cannot use throw with non-reference type %s in register v%d", 2157 registerType.toString(), register)); 2158 } 2159 2160 assert registerType.type != null; 2161 2162 if (!registerType.type.extendsClass(ClassPath.getClassDef("Ljava/lang/Throwable;"))) { 2163 throw new ValidationException(String.format("Cannot use throw with non-throwable type %s in register v%d", 2164 registerType.type.getClassType(), register)); 2165 } 2166 } 2167 2168 private void analyzeArrayDataOrSwitch(AnalyzedInstruction analyzedInstruction) { 2169 int dataAddressOffset = ((OffsetInstruction)analyzedInstruction.instruction).getTargetAddressOffset(); 2170 2171 int dataCodeAddress = this.getInstructionAddress(analyzedInstruction) + dataAddressOffset; 2172 AnalyzedInstruction dataAnalyzedInstruction = instructions.get(dataCodeAddress); 2173 2174 if (dataAnalyzedInstruction != null) { 2175 dataAnalyzedInstruction.dead = false; 2176 2177 //if there is a preceding nop, it's deadness should be the same 2178 AnalyzedInstruction priorInstruction = 2179 instructions.valueAt(dataAnalyzedInstruction.getInstructionIndex()-1); 2180 if (priorInstruction.getInstruction().opcode == Opcode.NOP && 2181 !priorInstruction.getInstruction().getFormat().variableSizeFormat) { 2182 2183 priorInstruction.dead = false; 2184 } 2185 } 2186 } 2187 2188 private void verifySwitch(AnalyzedInstruction analyzedInstruction, Format expectedSwitchDataFormat) { 2189 int register = ((SingleRegisterInstruction)analyzedInstruction.instruction).getRegisterA(); 2190 int switchCodeAddressOffset = ((OffsetInstruction)analyzedInstruction.instruction).getTargetAddressOffset(); 2191 2192 getAndCheckSourceRegister(analyzedInstruction, register, Primitive32BitCategories); 2193 2194 int switchDataCodeAddress = this.getInstructionAddress(analyzedInstruction) + switchCodeAddressOffset; 2195 AnalyzedInstruction switchDataAnalyzedInstruction = instructions.get(switchDataCodeAddress); 2196 2197 if (switchDataAnalyzedInstruction == null || 2198 switchDataAnalyzedInstruction.instruction.getFormat() != expectedSwitchDataFormat) { 2199 throw new ValidationException(String.format("There is no %s structure at code address 0x%x", 2200 expectedSwitchDataFormat.name(), switchDataCodeAddress)); 2201 } 2202 } 2203 2204 private void analyzeFloatWideCmp(AnalyzedInstruction analyzedInstruction) { 2205 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 2206 RegisterType.getRegisterType(RegisterType.Category.Byte, null)); 2207 } 2208 2209 private void verifyFloatWideCmp(AnalyzedInstruction analyzedInstruction, EnumSet validCategories) { 2210 ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; 2211 2212 getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), validCategories); 2213 getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), validCategories); 2214 } 2215 2216 private void verifyIfEqNe(AnalyzedInstruction analyzedInstruction) { 2217 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; 2218 2219 RegisterType registerType1 = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA()); 2220 assert registerType1 != null; 2221 2222 RegisterType registerType2 = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); 2223 assert registerType2 != null; 2224 2225 if (!( 2226 (ReferenceCategories.contains(registerType1.category) && 2227 ReferenceCategories.contains(registerType2.category)) 2228 || 2229 (Primitive32BitCategories.contains(registerType1.category) && 2230 Primitive32BitCategories.contains(registerType2.category)) 2231 )) { 2232 2233 throw new ValidationException(String.format("%s cannot be used on registers of dissimilar types %s and " + 2234 "%s. They must both be a reference type or a primitive 32 bit type.", 2235 analyzedInstruction.instruction.opcode.name, registerType1.toString(), registerType2.toString())); 2236 } 2237 } 2238 2239 private void verifyIf(AnalyzedInstruction analyzedInstruction) { 2240 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; 2241 2242 getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), Primitive32BitCategories); 2243 getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), Primitive32BitCategories); 2244 } 2245 2246 private void verifyIfEqzNez(AnalyzedInstruction analyzedInstruction) { 2247 SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; 2248 2249 getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), 2250 ReferenceAndPrimitive32BitCategories); 2251 } 2252 2253 private void verifyIfz(AnalyzedInstruction analyzedInstruction) { 2254 SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; 2255 2256 getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), Primitive32BitCategories); 2257 } 2258 2259 private void analyze32BitPrimitiveAget(AnalyzedInstruction analyzedInstruction, 2260 RegisterType.Category instructionCategory) { 2261 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 2262 RegisterType.getRegisterType(instructionCategory, null)); 2263 } 2264 2265 private void verify32BitPrimitiveAget(AnalyzedInstruction analyzedInstruction, 2266 RegisterType.Category instructionCategory) { 2267 ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; 2268 2269 getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), Primitive32BitCategories); 2270 2271 RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); 2272 assert arrayRegisterType != null; 2273 2274 if (arrayRegisterType.category != RegisterType.Category.Null) { 2275 if (arrayRegisterType.category != RegisterType.Category.Reference) { 2276 throw new ValidationException(String.format("Cannot use %s with non-array type %s", 2277 analyzedInstruction.instruction.opcode.name, arrayRegisterType.category.toString())); 2278 } 2279 2280 assert arrayRegisterType.type != null; 2281 if (arrayRegisterType.type.getClassType().charAt(0) != '[') { 2282 throw new ValidationException(String.format("Cannot use %s with non-array type %s", 2283 analyzedInstruction.instruction.opcode.name, arrayRegisterType.type.getClassType())); 2284 } 2285 2286 assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef; 2287 ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type; 2288 2289 if (arrayClassDef.getArrayDimensions() != 1) { 2290 throw new ValidationException(String.format("Cannot use %s with multi-dimensional array type %s", 2291 analyzedInstruction.instruction.opcode.name, arrayRegisterType.type.getClassType())); 2292 } 2293 2294 RegisterType arrayBaseType = 2295 RegisterType.getRegisterTypeForType(arrayClassDef.getBaseElementClass().getClassType()); 2296 if (!checkArrayFieldAssignment(arrayBaseType.category, instructionCategory)) { 2297 throw new ValidationException(String.format("Cannot use %s with array type %s. Incorrect array type " + 2298 "for the instruction.", analyzedInstruction.instruction.opcode.name, 2299 arrayRegisterType.type.getClassType())); 2300 } 2301 } 2302 } 2303 2304 private void analyzeAgetWide(AnalyzedInstruction analyzedInstruction) { 2305 ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; 2306 2307 RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); 2308 assert arrayRegisterType != null; 2309 2310 if (arrayRegisterType.category != RegisterType.Category.Null) { 2311 assert arrayRegisterType.type != null; 2312 if (arrayRegisterType.type.getClassType().charAt(0) != '[') { 2313 throw new ValidationException(String.format("Cannot use aget-wide with non-array type %s", 2314 arrayRegisterType.type.getClassType())); 2315 } 2316 2317 assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef; 2318 ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type; 2319 2320 char arrayBaseType = arrayClassDef.getBaseElementClass().getClassType().charAt(0); 2321 if (arrayBaseType == 'J') { 2322 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 2323 RegisterType.getRegisterType(RegisterType.Category.LongLo, null)); 2324 } else if (arrayBaseType == 'D') { 2325 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 2326 RegisterType.getRegisterType(RegisterType.Category.DoubleLo, null)); 2327 } else { 2328 throw new ValidationException(String.format("Cannot use aget-wide with array type %s. Incorrect " + 2329 "array type for the instruction.", arrayRegisterType.type.getClassType())); 2330 } 2331 } else { 2332 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 2333 RegisterType.getRegisterType(RegisterType.Category.LongLo, null)); 2334 } 2335 } 2336 2337 private void verifyAgetWide(AnalyzedInstruction analyzedInstruction) { 2338 ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; 2339 2340 getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), Primitive32BitCategories); 2341 2342 RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); 2343 assert arrayRegisterType != null; 2344 2345 if (arrayRegisterType.category != RegisterType.Category.Null) { 2346 if (arrayRegisterType.category != RegisterType.Category.Reference) { 2347 throw new ValidationException(String.format("Cannot use aget-wide with non-array type %s", 2348 arrayRegisterType.category.toString())); 2349 } 2350 2351 assert arrayRegisterType.type != null; 2352 if (arrayRegisterType.type.getClassType().charAt(0) != '[') { 2353 throw new ValidationException(String.format("Cannot use aget-wide with non-array type %s", 2354 arrayRegisterType.type.getClassType())); 2355 } 2356 2357 assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef; 2358 ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type; 2359 2360 if (arrayClassDef.getArrayDimensions() != 1) { 2361 throw new ValidationException(String.format("Cannot use aget-wide with multi-dimensional array type %s", 2362 arrayRegisterType.type.getClassType())); 2363 } 2364 2365 char arrayBaseType = arrayClassDef.getBaseElementClass().getClassType().charAt(0); 2366 if (arrayBaseType != 'J' && arrayBaseType != 'D') { 2367 throw new ValidationException(String.format("Cannot use aget-wide with array type %s. Incorrect " + 2368 "array type for the instruction.", arrayRegisterType.type.getClassType())); 2369 } 2370 } 2371 } 2372 2373 private void analyzeAgetObject(AnalyzedInstruction analyzedInstruction) { 2374 ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; 2375 2376 RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); 2377 assert arrayRegisterType != null; 2378 2379 if (arrayRegisterType.category != RegisterType.Category.Null) { 2380 assert arrayRegisterType.type != null; 2381 if (arrayRegisterType.type.getClassType().charAt(0) != '[') { 2382 throw new ValidationException(String.format("Cannot use aget-object with non-array type %s", 2383 arrayRegisterType.type.getClassType())); 2384 } 2385 2386 assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef; 2387 ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type; 2388 2389 ClassPath.ClassDef elementClassDef = arrayClassDef.getImmediateElementClass(); 2390 char elementTypePrefix = elementClassDef.getClassType().charAt(0); 2391 if (elementTypePrefix != 'L' && elementTypePrefix != '[') { 2392 throw new ValidationException(String.format("Cannot use aget-object with array type %s. Incorrect " + 2393 "array type for the instruction.", arrayRegisterType.type.getClassType())); 2394 } 2395 2396 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 2397 RegisterType.getRegisterType(RegisterType.Category.Reference, elementClassDef)); 2398 } else { 2399 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 2400 RegisterType.getRegisterType(RegisterType.Category.Null, null)); 2401 } 2402 } 2403 2404 private void verifyAgetObject(AnalyzedInstruction analyzedInstruction) { 2405 ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; 2406 2407 getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), Primitive32BitCategories); 2408 2409 RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); 2410 assert arrayRegisterType != null; 2411 2412 if (arrayRegisterType.category != RegisterType.Category.Null) { 2413 if (arrayRegisterType.category != RegisterType.Category.Reference) { 2414 throw new ValidationException(String.format("Cannot use aget-object with non-array type %s", 2415 arrayRegisterType.category.toString())); 2416 } 2417 2418 assert arrayRegisterType.type != null; 2419 if (arrayRegisterType.type.getClassType().charAt(0) != '[') { 2420 throw new ValidationException(String.format("Cannot use aget-object with non-array type %s", 2421 arrayRegisterType.type.getClassType())); 2422 } 2423 2424 assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef; 2425 ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type; 2426 2427 ClassPath.ClassDef elementClassDef = arrayClassDef.getImmediateElementClass(); 2428 char elementTypePrefix = elementClassDef.getClassType().charAt(0); 2429 if (elementTypePrefix != 'L' && elementTypePrefix != '[') { 2430 throw new ValidationException(String.format("Cannot use aget-object with array type %s. Incorrect " + 2431 "array type for the instruction.", arrayRegisterType.type.getClassType())); 2432 } 2433 } 2434 } 2435 2436 private void verify32BitPrimitiveAput(AnalyzedInstruction analyzedInstruction, 2437 RegisterType.Category instructionCategory) { 2438 ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; 2439 2440 getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), Primitive32BitCategories); 2441 2442 RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA()); 2443 assert sourceRegisterType != null; 2444 RegisterType instructionRegisterType = RegisterType.getRegisterType(instructionCategory, null); 2445 if (!sourceRegisterType.canBeAssignedTo(instructionRegisterType)) { 2446 throw new ValidationException(String.format("Cannot use %s with source register type %s.", 2447 analyzedInstruction.instruction.opcode.name, sourceRegisterType.toString())); 2448 } 2449 2450 2451 RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); 2452 assert arrayRegisterType != null; 2453 2454 if (arrayRegisterType.category != RegisterType.Category.Null) { 2455 if (arrayRegisterType.category != RegisterType.Category.Reference) { 2456 throw new ValidationException(String.format("Cannot use %s with non-array type %s", 2457 analyzedInstruction.instruction.opcode.name, arrayRegisterType.category.toString())); 2458 } 2459 2460 assert arrayRegisterType.type != null; 2461 if (arrayRegisterType.type.getClassType().charAt(0) != '[') { 2462 throw new ValidationException(String.format("Cannot use %s with non-array type %s", 2463 analyzedInstruction.instruction.opcode.name, arrayRegisterType.type.getClassType())); 2464 } 2465 2466 assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef; 2467 ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type; 2468 2469 if (arrayClassDef.getArrayDimensions() != 1) { 2470 throw new ValidationException(String.format("Cannot use %s with multi-dimensional array type %s", 2471 analyzedInstruction.instruction.opcode.name, arrayRegisterType.type.getClassType())); 2472 } 2473 2474 RegisterType arrayBaseType = 2475 RegisterType.getRegisterTypeForType(arrayClassDef.getBaseElementClass().getClassType()); 2476 if (!checkArrayFieldAssignment(arrayBaseType.category, instructionCategory)) { 2477 throw new ValidationException(String.format("Cannot use %s with array type %s. Incorrect array type " + 2478 "for the instruction.", analyzedInstruction.instruction.opcode.name, 2479 arrayRegisterType.type.getClassType())); 2480 } 2481 } 2482 } 2483 2484 private void verifyAputWide(AnalyzedInstruction analyzedInstruction) { 2485 ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; 2486 2487 getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), Primitive32BitCategories); 2488 getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), WideLowCategories); 2489 2490 RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); 2491 assert arrayRegisterType != null; 2492 2493 if (arrayRegisterType.category != RegisterType.Category.Null) { 2494 if (arrayRegisterType.category != RegisterType.Category.Reference) { 2495 throw new ValidationException(String.format("Cannot use aput-wide with non-array type %s", 2496 arrayRegisterType.category.toString())); 2497 } 2498 2499 assert arrayRegisterType.type != null; 2500 if (arrayRegisterType.type.getClassType().charAt(0) != '[') { 2501 throw new ValidationException(String.format("Cannot use aput-wide with non-array type %s", 2502 arrayRegisterType.type.getClassType())); 2503 } 2504 2505 assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef; 2506 ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type; 2507 2508 if (arrayClassDef.getArrayDimensions() != 1) { 2509 throw new ValidationException(String.format("Cannot use aput-wide with multi-dimensional array type %s", 2510 arrayRegisterType.type.getClassType())); 2511 } 2512 2513 char arrayBaseType = arrayClassDef.getBaseElementClass().getClassType().charAt(0); 2514 if (arrayBaseType != 'J' && arrayBaseType != 'D') { 2515 throw new ValidationException(String.format("Cannot use aput-wide with array type %s. Incorrect " + 2516 "array type for the instruction.", arrayRegisterType.type.getClassType())); 2517 } 2518 } 2519 } 2520 2521 private void verifyAputObject(AnalyzedInstruction analyzedInstruction) { 2522 ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; 2523 2524 getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), Primitive32BitCategories); 2525 2526 RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA()); 2527 assert sourceRegisterType != null; 2528 2529 //TODO: ensure sourceRegisterType is a Reference type? 2530 2531 RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); 2532 assert arrayRegisterType != null; 2533 2534 if (arrayRegisterType.category != RegisterType.Category.Null) { 2535 //don't check the source type against the array type, just make sure it is an array of reference types 2536 2537 if (arrayRegisterType.category != RegisterType.Category.Reference) { 2538 throw new ValidationException(String.format("Cannot use aget-object with non-array type %s", 2539 arrayRegisterType.category.toString())); 2540 } 2541 2542 assert arrayRegisterType.type != null; 2543 if (arrayRegisterType.type.getClassType().charAt(0) != '[') { 2544 throw new ValidationException(String.format("Cannot use aget-object with non-array type %s", 2545 arrayRegisterType.type.getClassType())); 2546 } 2547 2548 assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef; 2549 ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type; 2550 2551 ClassPath.ClassDef elementClassDef = arrayClassDef.getImmediateElementClass(); 2552 char elementTypePrefix = elementClassDef.getClassType().charAt(0); 2553 if (elementTypePrefix != 'L' && elementTypePrefix != '[') { 2554 throw new ValidationException(String.format("Cannot use aget-object with array type %s. Incorrect " + 2555 "array type for the instruction.", arrayRegisterType.type.getClassType())); 2556 } 2557 } 2558 } 2559 2560 private void analyze32BitPrimitiveIget(AnalyzedInstruction analyzedInstruction, 2561 RegisterType.Category instructionCategory) { 2562 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 2563 RegisterType.getRegisterType(instructionCategory, null)); 2564 } 2565 2566 private void verify32BitPrimitiveIget(AnalyzedInstruction analyzedInstruction, 2567 RegisterType.Category instructionCategory) { 2568 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; 2569 2570 RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), 2571 ReferenceOrUninitThisCategories); 2572 2573 //TODO: check access 2574 Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); 2575 assert referencedItem instanceof FieldIdItem; 2576 FieldIdItem field = (FieldIdItem)referencedItem; 2577 2578 if (objectRegisterType.category != RegisterType.Category.Null && 2579 !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) { 2580 throw new ValidationException(String.format("Cannot access field %s through type %s", 2581 field.getFieldString(), objectRegisterType.type.getClassType())); 2582 } 2583 2584 RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); 2585 2586 if (!checkArrayFieldAssignment(fieldType.category, instructionCategory)) { 2587 throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + 2588 "for the instruction.", analyzedInstruction.instruction.opcode.name, 2589 field.getFieldString())); 2590 } 2591 } 2592 2593 private void analyzeIgetWideObject(AnalyzedInstruction analyzedInstruction) { 2594 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; 2595 2596 Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); 2597 assert referencedItem instanceof FieldIdItem; 2598 FieldIdItem field = (FieldIdItem)referencedItem; 2599 2600 RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); 2601 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, fieldType); 2602 } 2603 2604 private void verifyIgetWide(AnalyzedInstruction analyzedInstruction) { 2605 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; 2606 2607 RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), 2608 ReferenceOrUninitThisCategories); 2609 2610 //TODO: check access 2611 Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); 2612 assert referencedItem instanceof FieldIdItem; 2613 FieldIdItem field = (FieldIdItem)referencedItem; 2614 2615 if (objectRegisterType.category != RegisterType.Category.Null && 2616 !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) { 2617 throw new ValidationException(String.format("Cannot access field %s through type %s", 2618 field.getFieldString(), objectRegisterType.type.getClassType())); 2619 } 2620 2621 RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); 2622 2623 if (!WideLowCategories.contains(fieldType.category)) { 2624 throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + 2625 "for the instruction.", analyzedInstruction.instruction.opcode.name, 2626 field.getFieldString())); 2627 } 2628 } 2629 2630 private void verifyIgetObject(AnalyzedInstruction analyzedInstruction) { 2631 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; 2632 2633 RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), 2634 ReferenceOrUninitThisCategories); 2635 2636 //TODO: check access 2637 Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); 2638 assert referencedItem instanceof FieldIdItem; 2639 FieldIdItem field = (FieldIdItem)referencedItem; 2640 2641 if (objectRegisterType.category != RegisterType.Category.Null && 2642 !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) { 2643 throw new ValidationException(String.format("Cannot access field %s through type %s", 2644 field.getFieldString(), objectRegisterType.type.getClassType())); 2645 } 2646 2647 RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); 2648 2649 if (fieldType.category != RegisterType.Category.Reference) { 2650 throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + 2651 "for the instruction.", analyzedInstruction.instruction.opcode.name, 2652 field.getFieldString())); 2653 } 2654 } 2655 2656 private void verify32BitPrimitiveIput(AnalyzedInstruction analyzedInstruction, 2657 RegisterType.Category instructionCategory) { 2658 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; 2659 2660 RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), 2661 ReferenceOrUninitThisCategories); 2662 2663 RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA()); 2664 assert sourceRegisterType != null; 2665 2666 //per CodeVerify.c in dalvik: 2667 //java generates synthetic functions that write byte values into boolean fields 2668 if (sourceRegisterType.category == RegisterType.Category.Byte && 2669 instructionCategory == RegisterType.Category.Boolean) { 2670 2671 sourceRegisterType = RegisterType.getRegisterType(RegisterType.Category.Boolean, null); 2672 } 2673 2674 RegisterType instructionRegisterType = RegisterType.getRegisterType(instructionCategory, null); 2675 if (!sourceRegisterType.canBeAssignedTo(instructionRegisterType)) { 2676 throw new ValidationException(String.format("Cannot use %s with source register type %s.", 2677 analyzedInstruction.instruction.opcode.name, sourceRegisterType.toString())); 2678 } 2679 2680 2681 //TODO: check access 2682 Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); 2683 assert referencedItem instanceof FieldIdItem; 2684 FieldIdItem field = (FieldIdItem)referencedItem; 2685 2686 if (objectRegisterType.category != RegisterType.Category.Null && 2687 !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) { 2688 throw new ValidationException(String.format("Cannot access field %s through type %s", 2689 field.getFieldString(), objectRegisterType.type.getClassType())); 2690 } 2691 2692 RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); 2693 2694 if (!checkArrayFieldAssignment(fieldType.category, instructionCategory)) { 2695 throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + 2696 "for the instruction.", analyzedInstruction.instruction.opcode.name, 2697 field.getFieldString())); 2698 } 2699 } 2700 2701 private void verifyIputWide(AnalyzedInstruction analyzedInstruction) { 2702 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; 2703 2704 RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), 2705 ReferenceOrUninitThisCategories); 2706 2707 getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), WideLowCategories); 2708 2709 //TODO: check access 2710 Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); 2711 assert referencedItem instanceof FieldIdItem; 2712 FieldIdItem field = (FieldIdItem)referencedItem; 2713 2714 if (objectRegisterType.category != RegisterType.Category.Null && 2715 !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) { 2716 throw new ValidationException(String.format("Cannot access field %s through type %s", 2717 field.getFieldString(), objectRegisterType.type.getClassType())); 2718 } 2719 2720 RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); 2721 2722 if (!WideLowCategories.contains(fieldType.category)) { 2723 throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + 2724 "for the instruction.", analyzedInstruction.instruction.opcode.name, 2725 field.getFieldString())); 2726 } 2727 } 2728 2729 private void verifyIputObject(AnalyzedInstruction analyzedInstruction) { 2730 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; 2731 2732 RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), 2733 ReferenceOrUninitThisCategories); 2734 2735 RegisterType sourceRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), 2736 ReferenceCategories); 2737 2738 //TODO: check access 2739 Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); 2740 assert referencedItem instanceof FieldIdItem; 2741 FieldIdItem field = (FieldIdItem)referencedItem; 2742 2743 if (objectRegisterType.category != RegisterType.Category.Null && 2744 !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) { 2745 throw new ValidationException(String.format("Cannot access field %s through type %s", 2746 field.getFieldString(), objectRegisterType.type.getClassType())); 2747 } 2748 2749 RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); 2750 2751 if (fieldType.category != RegisterType.Category.Reference) { 2752 throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + 2753 "for the instruction.", analyzedInstruction.instruction.opcode.name, 2754 field.getFieldString())); 2755 } 2756 2757 if (sourceRegisterType.category != RegisterType.Category.Null && 2758 !fieldType.type.isInterface() && 2759 !sourceRegisterType.type.extendsClass(fieldType.type)) { 2760 2761 throw new ValidationException(String.format("Cannot store a value of type %s into a field of type %s", 2762 sourceRegisterType.type.getClassType(), fieldType.type.getClassType())); 2763 } 2764 } 2765 2766 private void analyze32BitPrimitiveSget(AnalyzedInstruction analyzedInstruction, 2767 RegisterType.Category instructionCategory) { 2768 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 2769 RegisterType.getRegisterType(instructionCategory, null)); 2770 } 2771 2772 private void verify32BitPrimitiveSget(AnalyzedInstruction analyzedInstruction, 2773 RegisterType.Category instructionCategory) { 2774 //TODO: check access 2775 Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); 2776 assert referencedItem instanceof FieldIdItem; 2777 FieldIdItem field = (FieldIdItem)referencedItem; 2778 2779 RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); 2780 2781 if (!checkArrayFieldAssignment(fieldType.category, instructionCategory)) { 2782 throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + 2783 "for the instruction.", analyzedInstruction.instruction.opcode.name, 2784 field.getFieldString())); 2785 } 2786 } 2787 2788 private void analyzeSgetWideObject(AnalyzedInstruction analyzedInstruction) { 2789 Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); 2790 assert referencedItem instanceof FieldIdItem; 2791 FieldIdItem field = (FieldIdItem)referencedItem; 2792 2793 RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); 2794 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, fieldType); 2795 } 2796 2797 private void verifySgetWide(AnalyzedInstruction analyzedInstruction) { 2798 //TODO: check access 2799 Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); 2800 assert referencedItem instanceof FieldIdItem; 2801 FieldIdItem field = (FieldIdItem)referencedItem; 2802 2803 RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); 2804 2805 2806 if (fieldType.category != RegisterType.Category.LongLo && 2807 fieldType.category != RegisterType.Category.DoubleLo) { 2808 2809 throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + 2810 "for the instruction.", analyzedInstruction.instruction.opcode.name, 2811 field.getFieldString())); 2812 } 2813 } 2814 2815 private void verifySgetObject(AnalyzedInstruction analyzedInstruction) { 2816 //TODO: check access 2817 Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); 2818 assert referencedItem instanceof FieldIdItem; 2819 FieldIdItem field = (FieldIdItem)referencedItem; 2820 2821 RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); 2822 2823 if (fieldType.category != RegisterType.Category.Reference) { 2824 throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + 2825 "for the instruction.", analyzedInstruction.instruction.opcode.name, 2826 field.getFieldString())); 2827 } 2828 } 2829 2830 private void verify32BitPrimitiveSput(AnalyzedInstruction analyzedInstruction, 2831 RegisterType.Category instructionCategory) { 2832 SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; 2833 2834 RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA()); 2835 assert sourceRegisterType != null; 2836 2837 //per CodeVerify.c in dalvik: 2838 //java generates synthetic functions that write byte values into boolean fields 2839 if (sourceRegisterType.category == RegisterType.Category.Byte && 2840 instructionCategory == RegisterType.Category.Boolean) { 2841 2842 sourceRegisterType = RegisterType.getRegisterType(RegisterType.Category.Boolean, null); 2843 } 2844 2845 RegisterType instructionRegisterType = RegisterType.getRegisterType(instructionCategory, null); 2846 if (!sourceRegisterType.canBeAssignedTo(instructionRegisterType)) { 2847 throw new ValidationException(String.format("Cannot use %s with source register type %s.", 2848 analyzedInstruction.instruction.opcode.name, sourceRegisterType.toString())); 2849 } 2850 2851 //TODO: check access 2852 Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); 2853 assert referencedItem instanceof FieldIdItem; 2854 FieldIdItem field = (FieldIdItem)referencedItem; 2855 2856 RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); 2857 2858 if (!checkArrayFieldAssignment(fieldType.category, instructionCategory)) { 2859 throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + 2860 "for the instruction.", analyzedInstruction.instruction.opcode.name, 2861 field.getFieldString())); 2862 } 2863 } 2864 2865 private void verifySputWide(AnalyzedInstruction analyzedInstruction) { 2866 SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; 2867 2868 2869 getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), WideLowCategories); 2870 2871 //TODO: check access 2872 Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); 2873 assert referencedItem instanceof FieldIdItem; 2874 FieldIdItem field = (FieldIdItem)referencedItem; 2875 2876 RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); 2877 2878 if (!WideLowCategories.contains(fieldType.category)) { 2879 throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + 2880 "for the instruction.", analyzedInstruction.instruction.opcode.name, 2881 field.getFieldString())); 2882 } 2883 } 2884 2885 private void verifySputObject(AnalyzedInstruction analyzedInstruction) { 2886 SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; 2887 2888 RegisterType sourceRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), 2889 ReferenceCategories); 2890 2891 //TODO: check access 2892 Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); 2893 assert referencedItem instanceof FieldIdItem; 2894 FieldIdItem field = (FieldIdItem)referencedItem; 2895 2896 RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); 2897 2898 if (fieldType.category != RegisterType.Category.Reference) { 2899 throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + 2900 "for the instruction.", analyzedInstruction.instruction.opcode.name, 2901 field.getFieldString())); 2902 } 2903 2904 if (sourceRegisterType.category != RegisterType.Category.Null && 2905 !fieldType.type.isInterface() && 2906 !sourceRegisterType.type.extendsClass(fieldType.type)) { 2907 2908 throw new ValidationException(String.format("Cannot store a value of type %s into a field of type %s", 2909 sourceRegisterType.type.getClassType(), fieldType.type.getClassType())); 2910 } 2911 } 2912 2913 private void analyzeInvokeDirect(AnalyzedInstruction analyzedInstruction) { 2914 FiveRegisterInstruction instruction = (FiveRegisterInstruction)analyzedInstruction.instruction; 2915 analyzeInvokeDirectCommon(analyzedInstruction, new Format35cRegisterIterator(instruction)); 2916 } 2917 2918 private void verifyInvoke(AnalyzedInstruction analyzedInstruction, int invokeType) { 2919 FiveRegisterInstruction instruction = (FiveRegisterInstruction)analyzedInstruction.instruction; 2920 verifyInvokeCommon(analyzedInstruction, false, invokeType, new Format35cRegisterIterator(instruction)); 2921 } 2922 2923 private void analyzeInvokeDirectRange(AnalyzedInstruction analyzedInstruction) { 2924 RegisterRangeInstruction instruction = (RegisterRangeInstruction)analyzedInstruction.instruction; 2925 analyzeInvokeDirectCommon(analyzedInstruction, new Format3rcRegisterIterator(instruction)); 2926 } 2927 2928 private void verifyInvokeRange(AnalyzedInstruction analyzedInstruction, int invokeType) { 2929 RegisterRangeInstruction instruction = (RegisterRangeInstruction)analyzedInstruction.instruction; 2930 verifyInvokeCommon(analyzedInstruction, true, invokeType, new Format3rcRegisterIterator(instruction)); 2931 } 2932 2933 private static final int INVOKE_VIRTUAL = 0x01; 2934 private static final int INVOKE_SUPER = 0x02; 2935 private static final int INVOKE_DIRECT = 0x04; 2936 private static final int INVOKE_INTERFACE = 0x08; 2937 private static final int INVOKE_STATIC = 0x10; 2938 2939 private void analyzeInvokeDirectCommon(AnalyzedInstruction analyzedInstruction, RegisterIterator registers) { 2940 //the only time that an invoke instruction changes a register type is when using invoke-direct on a 2941 //constructor (<init>) method, which changes the uninitialized reference (and any register that the same 2942 //uninit reference has been copied to) to an initialized reference 2943 2944 InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; 2945 2946 Item item = instruction.getReferencedItem(); 2947 assert item.getItemType() == ItemType.TYPE_METHOD_ID_ITEM; 2948 MethodIdItem methodIdItem = (MethodIdItem)item; 2949 2950 if (!methodIdItem.getMethodName().getStringValue().equals("<init>")) { 2951 return; 2952 } 2953 2954 RegisterType objectRegisterType; 2955 //the object register is always the first register 2956 int objectRegister = registers.getRegister(); 2957 2958 objectRegisterType = analyzedInstruction.getPreInstructionRegisterType(objectRegister); 2959 assert objectRegisterType != null; 2960 2961 if (objectRegisterType.category != RegisterType.Category.UninitRef && 2962 objectRegisterType.category != RegisterType.Category.UninitThis) { 2963 return; 2964 } 2965 2966 setPostRegisterTypeAndPropagateChanges(analyzedInstruction, objectRegister, 2967 RegisterType.getRegisterType(RegisterType.Category.Reference, objectRegisterType.type)); 2968 2969 for (int i=0; i<analyzedInstruction.postRegisterMap.length; i++) { 2970 RegisterType postInstructionRegisterType = analyzedInstruction.postRegisterMap[i]; 2971 if (postInstructionRegisterType.category == RegisterType.Category.Unknown) { 2972 RegisterType preInstructionRegisterType = 2973 analyzedInstruction.getPreInstructionRegisterType(i); 2974 2975 if (preInstructionRegisterType.category == RegisterType.Category.UninitRef || 2976 preInstructionRegisterType.category == RegisterType.Category.UninitThis) { 2977 2978 RegisterType registerType; 2979 if (preInstructionRegisterType == objectRegisterType) { 2980 registerType = analyzedInstruction.postRegisterMap[objectRegister]; 2981 } else { 2982 registerType = preInstructionRegisterType; 2983 } 2984 2985 setPostRegisterTypeAndPropagateChanges(analyzedInstruction, i, registerType); 2986 } 2987 } 2988 } 2989 } 2990 2991 private void verifyInvokeCommon(AnalyzedInstruction analyzedInstruction, boolean isRange, int invokeType, 2992 RegisterIterator registers) { 2993 InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; 2994 2995 //TODO: check access 2996 2997 Item item = instruction.getReferencedItem(); 2998 assert item.getItemType() == ItemType.TYPE_METHOD_ID_ITEM; 2999 MethodIdItem methodIdItem = (MethodIdItem)item; 3000 3001 TypeIdItem methodClass = methodIdItem.getContainingClass(); 3002 boolean isInit = false; 3003 3004 if (methodIdItem.getMethodName().getStringValue().charAt(0) == '<') { 3005 if ((invokeType & INVOKE_DIRECT) != 0) { 3006 isInit = true; 3007 } else { 3008 throw new ValidationException(String.format("Cannot call constructor %s with %s", 3009 methodIdItem.getMethodString(), analyzedInstruction.instruction.opcode.name)); 3010 } 3011 } 3012 3013 ClassPath.ClassDef methodClassDef = ClassPath.getClassDef(methodClass); 3014 if ((invokeType & INVOKE_INTERFACE) != 0) { 3015 if (!methodClassDef.isInterface()) { 3016 throw new ValidationException(String.format("Cannot call method %s with %s. %s is not an interface " + 3017 "class.", methodIdItem.getMethodString(), analyzedInstruction.instruction.opcode.name, 3018 methodClassDef.getClassType())); 3019 } 3020 } else { 3021 if (methodClassDef.isInterface()) { 3022 throw new ValidationException(String.format("Cannot call method %s with %s. %s is an interface class." + 3023 " Use invoke-interface or invoke-interface/range instead.", methodIdItem.getMethodString(), 3024 analyzedInstruction.instruction.opcode.name, methodClassDef.getClassType())); 3025 } 3026 } 3027 3028 if ((invokeType & INVOKE_SUPER) != 0) { 3029 ClassPath.ClassDef currentMethodClassDef = ClassPath.getClassDef(encodedMethod.method.getContainingClass()); 3030 if (currentMethodClassDef.getSuperclass() == null) { 3031 throw new ValidationException(String.format("Cannot call method %s with %s. %s has no superclass", 3032 methodIdItem.getMethodString(), analyzedInstruction.instruction.opcode.name, 3033 methodClassDef.getSuperclass().getClassType())); 3034 } 3035 3036 if (!currentMethodClassDef.getSuperclass().extendsClass(methodClassDef)) { 3037 throw new ValidationException(String.format("Cannot call method %s with %s. %s is not an ancestor " + 3038 "of the current class %s", methodIdItem.getMethodString(), 3039 analyzedInstruction.instruction.opcode.name, methodClass.getTypeDescriptor(), 3040 encodedMethod.method.getContainingClass().getTypeDescriptor())); 3041 } 3042 3043 if (!currentMethodClassDef.getSuperclass().hasVirtualMethod(methodIdItem.getVirtualMethodString())) { 3044 throw new ValidationException(String.format("Cannot call method %s with %s. The superclass %s has" + 3045 "no such method", methodIdItem.getMethodString(), 3046 analyzedInstruction.instruction.opcode.name, methodClassDef.getSuperclass().getClassType())); 3047 } 3048 } 3049 3050 assert isRange || registers.getCount() <= 5; 3051 3052 TypeListItem typeListItem = methodIdItem.getPrototype().getParameters(); 3053 int methodParameterRegisterCount; 3054 if (typeListItem == null) { 3055 methodParameterRegisterCount = 0; 3056 } else { 3057 methodParameterRegisterCount = typeListItem.getRegisterCount(); 3058 } 3059 3060 if ((invokeType & INVOKE_STATIC) == 0) { 3061 methodParameterRegisterCount++; 3062 } 3063 3064 if (methodParameterRegisterCount != registers.getCount()) { 3065 throw new ValidationException(String.format("The number of registers does not match the number of " + 3066 "parameters for method %s. Expecting %d registers, got %d.", methodIdItem.getMethodString(), 3067 methodParameterRegisterCount + 1, registers.getCount())); 3068 } 3069 3070 RegisterType objectRegisterType = null; 3071 int objectRegister = 0; 3072 if ((invokeType & INVOKE_STATIC) == 0) { 3073 objectRegister = registers.getRegister(); 3074 registers.moveNext(); 3075 3076 objectRegisterType = analyzedInstruction.getPreInstructionRegisterType(objectRegister); 3077 assert objectRegisterType != null; 3078 if (objectRegisterType.category == RegisterType.Category.UninitRef || 3079 objectRegisterType.category == RegisterType.Category.UninitThis) { 3080 3081 if (!isInit) { 3082 throw new ValidationException(String.format("Cannot invoke non-<init> method %s on uninitialized " + 3083 "reference type %s", methodIdItem.getMethodString(), 3084 objectRegisterType.type.getClassType())); 3085 } 3086 } else if (objectRegisterType.category == RegisterType.Category.Reference) { 3087 if (isInit) { 3088 throw new ValidationException(String.format("Cannot invoke %s on initialized reference type %s", 3089 methodIdItem.getMethodString(), objectRegisterType.type.getClassType())); 3090 } 3091 } else if (objectRegisterType.category == RegisterType.Category.Null) { 3092 if (isInit) { 3093 throw new ValidationException(String.format("Cannot invoke %s on a null reference", 3094 methodIdItem.getMethodString())); 3095 } 3096 } 3097 else { 3098 throw new ValidationException(String.format("Cannot invoke %s on non-reference type %s", 3099 methodIdItem.getMethodString(), objectRegisterType.toString())); 3100 } 3101 3102 if (isInit) { 3103 if (objectRegisterType.type.getSuperclass() == methodClassDef) { 3104 if (!encodedMethod.method.getMethodName().getStringValue().equals("<init>")) { 3105 throw new ValidationException(String.format("Cannot call %s on type %s. The object type must " + 3106 "match the method type exactly", methodIdItem.getMethodString(), 3107 objectRegisterType.type.getClassType())); 3108 } 3109 } 3110 } 3111 3112 if ((invokeType & INVOKE_INTERFACE) == 0 && objectRegisterType.category != RegisterType.Category.Null && 3113 !objectRegisterType.type.extendsClass(methodClassDef)) { 3114 3115 throw new ValidationException(String.format("Cannot call method %s on an object of type %s, which " + 3116 "does not extend %s.", methodIdItem.getMethodString(), objectRegisterType.type.getClassType(), 3117 methodClassDef.getClassType())); 3118 } 3119 } 3120 3121 if (typeListItem != null) { 3122 List<TypeIdItem> parameterTypes = typeListItem.getTypes(); 3123 int parameterTypeIndex = 0; 3124 while (!registers.pastEnd()) { 3125 assert parameterTypeIndex < parameterTypes.size(); 3126 RegisterType parameterType = 3127 RegisterType.getRegisterTypeForTypeIdItem(parameterTypes.get(parameterTypeIndex)); 3128 3129 int register = registers.getRegister(); 3130 3131 RegisterType parameterRegisterType; 3132 if (WideLowCategories.contains(parameterType.category)) { 3133 parameterRegisterType = getAndCheckSourceRegister(analyzedInstruction, register, WideLowCategories); 3134 3135 if (!registers.moveNext()) { 3136 throw new ValidationException(String.format("No 2nd register specified for wide register pair v%d", 3137 parameterTypeIndex+1)); 3138 } 3139 int nextRegister = registers.getRegister(); 3140 3141 if (nextRegister != register + 1) { 3142 throw new ValidationException(String.format("Invalid wide register pair (v%d, v%d). Registers " + 3143 "must be consecutive.", register, nextRegister)); 3144 } 3145 } else { 3146 parameterRegisterType = analyzedInstruction.getPreInstructionRegisterType(register); 3147 } 3148 3149 assert parameterRegisterType != null; 3150 3151 if (!parameterRegisterType.canBeAssignedTo(parameterType)) { 3152 throw new ValidationException( 3153 String.format("Invalid register type %s for parameter %d %s.", 3154 parameterRegisterType.toString(), parameterTypeIndex+1, 3155 parameterType.toString())); 3156 } 3157 3158 parameterTypeIndex++; 3159 registers.moveNext(); 3160 } 3161 } 3162 } 3163 3164 private void analyzeUnaryOp(AnalyzedInstruction analyzedInstruction, RegisterType.Category destRegisterCategory) { 3165 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 3166 RegisterType.getRegisterType(destRegisterCategory, null)); 3167 } 3168 3169 private void verifyUnaryOp(AnalyzedInstruction analyzedInstruction, EnumSet validSourceCategories) { 3170 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; 3171 3172 getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), validSourceCategories); 3173 } 3174 3175 private void analyzeBinaryOp(AnalyzedInstruction analyzedInstruction, RegisterType.Category destRegisterCategory, 3176 boolean checkForBoolean) { 3177 if (checkForBoolean) { 3178 ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; 3179 3180 RegisterType source1RegisterType = 3181 analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); 3182 RegisterType source2RegisterType = 3183 analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterC()); 3184 3185 if (BooleanCategories.contains(source1RegisterType.category) && 3186 BooleanCategories.contains(source2RegisterType.category)) { 3187 3188 destRegisterCategory = RegisterType.Category.Boolean; 3189 } 3190 } 3191 3192 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 3193 RegisterType.getRegisterType(destRegisterCategory, null)); 3194 } 3195 3196 private void verifyBinaryOp(AnalyzedInstruction analyzedInstruction, EnumSet validSource1Categories, 3197 EnumSet validSource2Categories) { 3198 ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; 3199 3200 getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), validSource1Categories); 3201 getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), validSource2Categories); 3202 } 3203 3204 private void analyzeBinary2AddrOp(AnalyzedInstruction analyzedInstruction, 3205 RegisterType.Category destRegisterCategory, boolean checkForBoolean) { 3206 if (checkForBoolean) { 3207 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; 3208 3209 RegisterType source1RegisterType = 3210 analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA()); 3211 RegisterType source2RegisterType = 3212 analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); 3213 3214 if (BooleanCategories.contains(source1RegisterType.category) && 3215 BooleanCategories.contains(source2RegisterType.category)) { 3216 3217 destRegisterCategory = RegisterType.Category.Boolean; 3218 } 3219 } 3220 3221 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 3222 RegisterType.getRegisterType(destRegisterCategory, null)); 3223 } 3224 3225 private void verifyBinary2AddrOp(AnalyzedInstruction analyzedInstruction, EnumSet validSource1Categories, 3226 EnumSet validSource2Categories) { 3227 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; 3228 3229 getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), validSource1Categories); 3230 getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), validSource2Categories); 3231 } 3232 3233 private void analyzeLiteralBinaryOp(AnalyzedInstruction analyzedInstruction, 3234 RegisterType.Category destRegisterCategory, boolean checkForBoolean) { 3235 if (checkForBoolean) { 3236 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; 3237 3238 RegisterType sourceRegisterType = 3239 analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); 3240 3241 if (BooleanCategories.contains(sourceRegisterType.category)) { 3242 long literal = ((LiteralInstruction)analyzedInstruction.instruction).getLiteral(); 3243 if (literal == 0 || literal == 1) { 3244 destRegisterCategory = RegisterType.Category.Boolean; 3245 } 3246 } 3247 } 3248 3249 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 3250 RegisterType.getRegisterType(destRegisterCategory, null)); 3251 } 3252 3253 private void verifyLiteralBinaryOp(AnalyzedInstruction analyzedInstruction) { 3254 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; 3255 3256 getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), Primitive32BitCategories); 3257 } 3258 3259 private RegisterType.Category getDestTypeForLiteralShiftRight(AnalyzedInstruction analyzedInstruction, 3260 boolean signedShift) { 3261 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; 3262 3263 RegisterType sourceRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), 3264 Primitive32BitCategories); 3265 long literalShift = ((LiteralInstruction)analyzedInstruction.instruction).getLiteral(); 3266 3267 if (literalShift == 0) { 3268 return sourceRegisterType.category; 3269 } 3270 3271 RegisterType.Category destRegisterCategory; 3272 if (!signedShift) { 3273 destRegisterCategory = RegisterType.Category.Integer; 3274 } else { 3275 destRegisterCategory = sourceRegisterType.category; 3276 } 3277 3278 if (literalShift >= 32) { 3279 //TODO: add warning 3280 return destRegisterCategory; 3281 } 3282 3283 switch (sourceRegisterType.category) { 3284 case Integer: 3285 case Float: 3286 if (!signedShift) { 3287 if (literalShift > 24) { 3288 return RegisterType.Category.PosByte; 3289 } 3290 if (literalShift >= 16) { 3291 return RegisterType.Category.Char; 3292 } 3293 } else { 3294 if (literalShift >= 24) { 3295 return RegisterType.Category.Byte; 3296 } 3297 if (literalShift >= 16) { 3298 return RegisterType.Category.Short; 3299 } 3300 } 3301 break; 3302 case Short: 3303 if (signedShift && literalShift >= 8) { 3304 return RegisterType.Category.Byte; 3305 } 3306 break; 3307 case PosShort: 3308 if (literalShift >= 8) { 3309 return RegisterType.Category.PosByte; 3310 } 3311 break; 3312 case Char: 3313 if (literalShift > 8) { 3314 return RegisterType.Category.PosByte; 3315 } 3316 break; 3317 case Byte: 3318 break; 3319 case PosByte: 3320 return RegisterType.Category.PosByte; 3321 case Null: 3322 case One: 3323 case Boolean: 3324 return RegisterType.Category.Null; 3325 default: 3326 assert false; 3327 } 3328 3329 return destRegisterCategory; 3330 } 3331 3332 3333 private void analyzeExecuteInline(AnalyzedInstruction analyzedInstruction) { 3334 if (deodexUtil == null) { 3335 throw new ValidationException("Cannot analyze an odexed instruction unless we are deodexing"); 3336 } 3337 3338 Instruction35ms instruction = (Instruction35ms)analyzedInstruction.instruction; 3339 3340 DeodexUtil.InlineMethod inlineMethod = deodexUtil.lookupInlineMethod(analyzedInstruction); 3341 MethodIdItem inlineMethodIdItem = inlineMethod.getMethodIdItem(); 3342 if (inlineMethodIdItem == null) { 3343 throw new ValidationException(String.format("Cannot load inline method with index %d", 3344 instruction.getMethodIndex())); 3345 } 3346 3347 Opcode deodexedOpcode = null; 3348 switch (inlineMethod.methodType) { 3349 case DeodexUtil.Direct: 3350 deodexedOpcode = Opcode.INVOKE_DIRECT; 3351 break; 3352 case DeodexUtil.Static: 3353 deodexedOpcode = Opcode.INVOKE_STATIC; 3354 break; 3355 case DeodexUtil.Virtual: 3356 deodexedOpcode = Opcode.INVOKE_VIRTUAL; 3357 break; 3358 default: 3359 assert false; 3360 } 3361 3362 Instruction35c deodexedInstruction = new Instruction35c(deodexedOpcode, instruction.getRegCount(), 3363 instruction.getRegisterD(), instruction.getRegisterE(), instruction.getRegisterF(), 3364 instruction.getRegisterG(), instruction.getRegisterA(), inlineMethodIdItem); 3365 3366 analyzedInstruction.setDeodexedInstruction(deodexedInstruction); 3367 3368 analyzeInstruction(analyzedInstruction); 3369 } 3370 3371 private void analyzeExecuteInlineRange(AnalyzedInstruction analyzedInstruction) { 3372 if (deodexUtil == null) { 3373 throw new ValidationException("Cannot analyze an odexed instruction unless we are deodexing"); 3374 } 3375 3376 Instruction3rms instruction = (Instruction3rms)analyzedInstruction.instruction; 3377 3378 DeodexUtil.InlineMethod inlineMethod = deodexUtil.lookupInlineMethod(analyzedInstruction); 3379 MethodIdItem inlineMethodIdItem = inlineMethod.getMethodIdItem(); 3380 if (inlineMethodIdItem == null) { 3381 throw new ValidationException(String.format("Cannot load inline method with index %d", 3382 instruction.getMethodIndex())); 3383 } 3384 3385 Opcode deodexedOpcode = null; 3386 switch (inlineMethod.methodType) { 3387 case DeodexUtil.Direct: 3388 deodexedOpcode = Opcode.INVOKE_DIRECT_RANGE; 3389 break; 3390 case DeodexUtil.Static: 3391 deodexedOpcode = Opcode.INVOKE_STATIC_RANGE; 3392 break; 3393 case DeodexUtil.Virtual: 3394 deodexedOpcode = Opcode.INVOKE_VIRTUAL_RANGE; 3395 break; 3396 default: 3397 assert false; 3398 } 3399 3400 Instruction3rc deodexedInstruction = new Instruction3rc(deodexedOpcode, instruction.getRegCount(), 3401 instruction.getStartRegister(), inlineMethodIdItem); 3402 3403 analyzedInstruction.setDeodexedInstruction(deodexedInstruction); 3404 3405 analyzeInstruction(analyzedInstruction); 3406 } 3407 3408 private void analyzeInvokeDirectEmpty(AnalyzedInstruction analyzedInstruction) { 3409 Instruction35s instruction = (Instruction35s)analyzedInstruction.instruction; 3410 3411 Instruction35c deodexedInstruction = new Instruction35c(Opcode.INVOKE_DIRECT, instruction.getRegCount(), 3412 instruction.getRegisterD(), instruction.getRegisterE(), instruction.getRegisterF(), 3413 instruction.getRegisterG(), instruction.getRegisterA(), instruction.getReferencedItem()); 3414 3415 analyzedInstruction.setDeodexedInstruction(deodexedInstruction); 3416 3417 analyzeInstruction(analyzedInstruction); 3418 } 3419 3420 private boolean analyzeIputIgetQuick(AnalyzedInstruction analyzedInstruction) { 3421 Instruction22cs instruction = (Instruction22cs)analyzedInstruction.instruction; 3422 3423 int fieldOffset = instruction.getFieldOffset(); 3424 RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), 3425 ReferenceOrUninitCategories); 3426 3427 if (objectRegisterType.category == RegisterType.Category.Null) { 3428 return false; 3429 } 3430 3431 FieldIdItem fieldIdItem = deodexUtil.lookupField(objectRegisterType.type, fieldOffset); 3432 if (fieldIdItem == null) { 3433 throw new ValidationException(String.format("Could not resolve the field in class %s at offset %d", 3434 objectRegisterType.type.getClassType(), fieldOffset)); 3435 } 3436 3437 String fieldType = fieldIdItem.getFieldType().getTypeDescriptor(); 3438 3439 Opcode opcode = OdexedFieldInstructionMapper.getAndCheckDeodexedOpcodeForOdexedOpcode(fieldType, instruction.opcode); 3440 3441 Instruction22c deodexedInstruction = new Instruction22c(opcode, (byte)instruction.getRegisterA(), 3442 (byte)instruction.getRegisterB(), fieldIdItem); 3443 analyzedInstruction.setDeodexedInstruction(deodexedInstruction); 3444 3445 analyzeInstruction(analyzedInstruction); 3446 3447 return true; 3448 } 3449 3450 private boolean analyzeInvokeVirtualQuick(AnalyzedInstruction analyzedInstruction, boolean isSuper, 3451 boolean isRange) { 3452 int methodIndex; 3453 int objectRegister; 3454 3455 3456 if (isRange) { 3457 Instruction3rms instruction = (Instruction3rms)analyzedInstruction.instruction; 3458 methodIndex = instruction.getMethodIndex(); 3459 objectRegister = instruction.getStartRegister(); 3460 } else { 3461 Instruction35ms instruction = (Instruction35ms)analyzedInstruction.instruction; 3462 methodIndex = instruction.getMethodIndex(); 3463 objectRegister = instruction.getRegisterD(); 3464 } 3465 3466 RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, objectRegister, 3467 ReferenceOrUninitCategories); 3468 3469 if (objectRegisterType.category == RegisterType.Category.Null) { 3470 return false; 3471 } 3472 3473 MethodIdItem methodIdItem = null; 3474 if (isSuper) { 3475 ClassPath.ClassDef classDef = ClassPath.getClassDef(this.encodedMethod.method.getContainingClass(), false); 3476 assert classDef != null; 3477 3478 if (classDef.getSuperclass() != null) { 3479 methodIdItem = deodexUtil.lookupVirtualMethod(classDef.getSuperclass(), methodIndex); 3480 } 3481 3482 if (methodIdItem == null) { 3483 //it's possible that the pre-odexed instruction had used the method from the current class instead 3484 //of from the superclass (although the superclass method is still what would actually be called). 3485 //And so the MethodIdItem for the superclass method may not be in the dex file. Let's try to get the 3486 //MethodIdItem for the method in the current class instead 3487 methodIdItem = deodexUtil.lookupVirtualMethod(classDef, methodIndex); 3488 } 3489 } else{ 3490 methodIdItem = deodexUtil.lookupVirtualMethod(objectRegisterType.type, methodIndex); 3491 } 3492 3493 if (methodIdItem == null) { 3494 throw new ValidationException(String.format("Could not resolve the method in class %s at index %d", 3495 objectRegisterType.type.getClassType(), methodIndex)); 3496 } 3497 3498 3499 Instruction deodexedInstruction; 3500 if (isRange) { 3501 Instruction3rms instruction = (Instruction3rms)analyzedInstruction.instruction; 3502 Opcode opcode; 3503 if (isSuper) { 3504 opcode = Opcode.INVOKE_SUPER_RANGE; 3505 } else { 3506 opcode = Opcode.INVOKE_VIRTUAL_RANGE; 3507 } 3508 3509 deodexedInstruction = new Instruction3rc(opcode, instruction.getRegCount(), 3510 instruction.getStartRegister(), methodIdItem); 3511 } else { 3512 Instruction35ms instruction = (Instruction35ms)analyzedInstruction.instruction; 3513 Opcode opcode; 3514 if (isSuper) { 3515 opcode = Opcode.INVOKE_SUPER; 3516 } else { 3517 opcode = Opcode.INVOKE_VIRTUAL; 3518 } 3519 3520 deodexedInstruction = new Instruction35c(opcode, instruction.getRegCount(), 3521 instruction.getRegisterD(), instruction.getRegisterE(), instruction.getRegisterF(), 3522 instruction.getRegisterG(), instruction.getRegisterA(), methodIdItem); 3523 } 3524 3525 analyzedInstruction.setDeodexedInstruction(deodexedInstruction); 3526 analyzeInstruction(analyzedInstruction); 3527 3528 return true; 3529 } 3530 3531 private boolean analyzePutGetVolatile(AnalyzedInstruction analyzedInstruction) { 3532 FieldIdItem fieldIdItem = 3533 (FieldIdItem)(((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem()); 3534 3535 String fieldType = fieldIdItem.getFieldType().getTypeDescriptor(); 3536 3537 Opcode opcode = OdexedFieldInstructionMapper.getAndCheckDeodexedOpcodeForOdexedOpcode(fieldType, 3538 analyzedInstruction.instruction.opcode); 3539 3540 Instruction deodexedInstruction; 3541 3542 if (analyzedInstruction.instruction.opcode.isOdexedStaticVolatile()) { 3543 SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; 3544 3545 deodexedInstruction = new Instruction21c(opcode, (byte)instruction.getRegisterA(), 3546 fieldIdItem); 3547 } else { 3548 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; 3549 3550 deodexedInstruction = new Instruction22c(opcode, (byte)instruction.getRegisterA(), 3551 (byte)instruction.getRegisterB(), fieldIdItem); 3552 } 3553 3554 analyzedInstruction.setDeodexedInstruction(deodexedInstruction); 3555 analyzeInstruction(analyzedInstruction); 3556 3557 return true; 3558 } 3559 3560 private static boolean checkArrayFieldAssignment(RegisterType.Category arrayFieldCategory, 3561 RegisterType.Category instructionCategory) { 3562 if (arrayFieldCategory == instructionCategory) { 3563 return true; 3564 } 3565 3566 if ((arrayFieldCategory == RegisterType.Category.Integer && 3567 instructionCategory == RegisterType.Category.Float) || 3568 (arrayFieldCategory == RegisterType.Category.Float && 3569 instructionCategory == RegisterType.Category.Integer)) { 3570 return true; 3571 } 3572 return false; 3573 } 3574 3575 private static RegisterType getAndCheckSourceRegister(AnalyzedInstruction analyzedInstruction, int registerNumber, 3576 EnumSet validCategories) { 3577 assert registerNumber >= 0 && registerNumber < analyzedInstruction.postRegisterMap.length; 3578 3579 RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(registerNumber); 3580 assert registerType != null; 3581 3582 checkRegister(registerType, registerNumber, validCategories); 3583 3584 if (validCategories == WideLowCategories) { 3585 checkRegister(registerType, registerNumber, WideLowCategories); 3586 checkWidePair(registerNumber, analyzedInstruction); 3587 3588 RegisterType secondRegisterType = analyzedInstruction.getPreInstructionRegisterType(registerNumber + 1); 3589 assert secondRegisterType != null; 3590 checkRegister(secondRegisterType, registerNumber+1, WideHighCategories); 3591 } 3592 3593 return registerType; 3594 } 3595 3596 private static void checkRegister(RegisterType registerType, int registerNumber, EnumSet validCategories) { 3597 if (!validCategories.contains(registerType.category)) { 3598 throw new ValidationException(String.format("Invalid register type %s for register v%d.", 3599 registerType.toString(), registerNumber)); 3600 } 3601 } 3602 3603 private static void checkWidePair(int registerNumber, AnalyzedInstruction analyzedInstruction) { 3604 if (registerNumber + 1 >= analyzedInstruction.postRegisterMap.length) { 3605 throw new ValidationException(String.format("v%d cannot be used as the first register in a wide register" + 3606 "pair because it is the last register.", registerNumber)); 3607 } 3608 } 3609 3610 private static interface RegisterIterator { 3611 int getRegister(); 3612 boolean moveNext(); 3613 int getCount(); 3614 boolean pastEnd(); 3615 } 3616 3617 private static class Format35cRegisterIterator implements RegisterIterator { 3618 private final int registerCount; 3619 private final int[] registers; 3620 private int currentRegister = 0; 3621 3622 public Format35cRegisterIterator(FiveRegisterInstruction instruction) { 3623 registerCount = instruction.getRegCount(); 3624 registers = new int[]{instruction.getRegisterD(), instruction.getRegisterE(), 3625 instruction.getRegisterF(), instruction.getRegisterG(), 3626 instruction.getRegisterA()}; 3627 } 3628 3629 public int getRegister() { 3630 return registers[currentRegister]; 3631 } 3632 3633 public boolean moveNext() { 3634 currentRegister++; 3635 return !pastEnd(); 3636 } 3637 3638 public int getCount() { 3639 return registerCount; 3640 } 3641 3642 public boolean pastEnd() { 3643 return currentRegister >= registerCount; 3644 } 3645 } 3646 3647 private static class Format3rcRegisterIterator implements RegisterIterator { 3648 private final int startRegister; 3649 private final int registerCount; 3650 private int currentRegister = 0; 3651 3652 public Format3rcRegisterIterator(RegisterRangeInstruction instruction) { 3653 startRegister = instruction.getStartRegister(); 3654 registerCount = instruction.getRegCount(); 3655 } 3656 3657 public int getRegister() { 3658 return startRegister + currentRegister; 3659 } 3660 3661 public boolean moveNext() { 3662 currentRegister++; 3663 return !pastEnd(); 3664 } 3665 3666 public int getCount() { 3667 return registerCount; 3668 } 3669 3670 public boolean pastEnd() { 3671 return currentRegister >= registerCount; 3672 } 3673 } 3674 } 3675