MethodAnalyzer.java revision f1a74cea19f10e9059e05f1cee6ae45baf118108
1package org.jf.dexlib.Code.Analysis; 2 3import org.jf.dexlib.*; 4import org.jf.dexlib.Code.*; 5import org.jf.dexlib.Code.Format.ArrayDataPseudoInstruction; 6import org.jf.dexlib.Code.Format.Format; 7import org.jf.dexlib.Util.*; 8 9import java.util.*; 10 11public class MethodAnalyzer { 12 private final ClassDataItem.EncodedMethod encodedMethod; 13 14 private SparseArray<AnalyzedInstruction> instructions; 15 16 private boolean analyzed = false; 17 18 //This is a dummy instruction that occurs immediately before the first real instruction. We can initialize the 19 //register types for this instruction to the parameter types, in order to have them propagate to all of its 20 //successors, e.g. the first real instruction, the first instructions in any exception handlers covering the first 21 //instruction, etc. 22 private AnalyzedInstruction startOfMethod; 23 24 public MethodAnalyzer(ClassDataItem.EncodedMethod encodedMethod) { 25 if (encodedMethod == null) { 26 throw new IllegalArgumentException("encodedMethod cannot be null"); 27 } 28 if (encodedMethod.codeItem == null || encodedMethod.codeItem.getInstructions().length == 0) { 29 throw new IllegalArgumentException("The method has no code"); 30 } 31 this.encodedMethod = encodedMethod; 32 buildInstructionList(); 33 34 //override AnalyzedInstruction and provide custom implementations of some of the methods, so that we don't 35 //have to handle the case this special case of instruction being null, in the main class 36 startOfMethod = new AnalyzedInstruction(null, -1, encodedMethod.codeItem.getRegisterCount()) { 37 public boolean setsRegister() { 38 return false; 39 } 40 41 @Override 42 public boolean setsWideRegister() { 43 return false; 44 } 45 46 @Override 47 public boolean setsRegister(int registerNumber) { 48 return false; 49 } 50 51 @Override 52 public int getDestinationRegister() { 53 assert false; 54 return -1; 55 }; 56 }; 57 } 58 59 public AnalyzedInstruction[] analyze() { 60 assert encodedMethod != null; 61 assert encodedMethod.codeItem != null; 62 63 if (analyzed) { 64 return makeInstructionArray(); 65 } 66 67 CodeItem codeItem = encodedMethod.codeItem; 68 MethodIdItem methodIdItem = encodedMethod.method; 69 70 int totalRegisters = codeItem.getRegisterCount(); 71 int parameterRegisters = methodIdItem.getPrototype().getParameterRegisterCount(); 72 73 //if this isn't a static method, determine which register is the "this" register and set the type to the 74 //current class 75 if ((encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) == 0) { 76 int thisRegister = totalRegisters - parameterRegisters - 1; 77 78 //if this is a constructor, then set the "this" register to an uninitialized reference of the current class 79 if ((encodedMethod.accessFlags & AccessFlags.CONSTRUCTOR.getValue()) != 0) { 80 //TODO: it would probably make more sense to validate this somewhere else, and just put an assert here. Also, need to do a similar check for static constructor 81 if (!encodedMethod.method.getMethodName().equals("<init>")) { 82 throw new ValidationException("The constructor flag can only be used with an <init> method."); 83 } 84 85 setRegisterTypeAndPropagateChanges(startOfMethod, thisRegister, 86 RegisterType.getRegisterType(RegisterType.Category.UninitRef, 87 ClassPath.getClassDef(methodIdItem.getContainingClass()))); 88 } else { 89 if (encodedMethod.method.getMethodName().equals("<init>")) { 90 throw new ValidationException("An <init> method must have the \"constructor\" access flag"); 91 } 92 93 setRegisterTypeAndPropagateChanges(startOfMethod, thisRegister, 94 RegisterType.getRegisterType(RegisterType.Category.Reference, 95 ClassPath.getClassDef(methodIdItem.getContainingClass()))); 96 } 97 } 98 99 TypeListItem parameters = methodIdItem.getPrototype().getParameters(); 100 if (parameters != null) { 101 RegisterType[] parameterTypes = getParameterTypes(parameters, parameterRegisters); 102 for (int i=0; i<parameterTypes.length; i++) { 103 RegisterType registerType = parameterTypes[i]; 104 int registerNum = (totalRegisters - parameterRegisters) + i; 105 setRegisterTypeAndPropagateChanges(startOfMethod, registerNum, registerType); 106 } 107 } 108 109 analyzed = true; 110 return makeInstructionArray(); 111 } 112 113 private int getThisRegister() { 114 assert (encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) == 0; 115 116 CodeItem codeItem = encodedMethod.codeItem; 117 assert codeItem != null; 118 119 MethodIdItem methodIdItem = encodedMethod.method; 120 assert methodIdItem != null; 121 122 int totalRegisters = codeItem.getRegisterCount(); 123 if (totalRegisters == 0) { 124 throw new ValidationException("A non-static method must have at least 1 register"); 125 } 126 127 int parameterRegisters = methodIdItem.getPrototype().getParameterRegisterCount(); 128 129 return totalRegisters - parameterRegisters - 1; 130 } 131 132 private boolean isInstanceConstructor() { 133 return (encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) == 0 && 134 (encodedMethod.accessFlags & AccessFlags.CONSTRUCTOR.getValue()) != 0; 135 } 136 137 private boolean isStaticConstructor() { 138 return (encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) != 0 && 139 (encodedMethod.accessFlags & AccessFlags.CONSTRUCTOR.getValue()) != 0; 140 } 141 142 public AnalyzedInstruction[] makeInstructionArray() { 143 AnalyzedInstruction[] instructionArray = new AnalyzedInstruction[instructions.size()]; 144 for (int i=0; i<instructions.size(); i++) { 145 instructionArray[i] = instructions.valueAt(i); 146 } 147 return instructionArray; 148 } 149 150 private static RegisterType[] getParameterTypes(TypeListItem typeListItem, int parameterRegisterCount) { 151 assert typeListItem != null; 152 assert parameterRegisterCount == typeListItem.getRegisterCount(); 153 154 RegisterType[] registerTypes = new RegisterType[parameterRegisterCount]; 155 156 int registerNum = 0; 157 for (TypeIdItem type: typeListItem.getTypes()) { 158 if (type.getRegisterCount() == 2) { 159 registerTypes[registerNum++] = RegisterType.getWideRegisterTypeForTypeIdItem(type, true); 160 registerTypes[registerNum++] = RegisterType.getWideRegisterTypeForTypeIdItem(type, false); 161 } else { 162 registerTypes[registerNum] = RegisterType.getRegisterTypeForTypeIdItem(type); 163 } 164 } 165 166 return registerTypes; 167 } 168 169 private int getInstructionAddress(AnalyzedInstruction instruction) { 170 return instructions.keyAt(instruction.instructionIndex); 171 } 172 173 private void setWideDestinationRegisterTypeAndPropagateChanges(AnalyzedInstruction analyzedInstruction, 174 RegisterType registerType) { 175 assert registerType.category == RegisterType.Category.LongLo || 176 registerType.category == RegisterType.Category.DoubleLo; 177 178 checkWideDestinationPair(analyzedInstruction); 179 180 setRegisterTypeAndPropagateChanges(analyzedInstruction, analyzedInstruction.getDestinationRegister(), 181 registerType); 182 if (registerType.category == RegisterType.Category.LongLo) { 183 setRegisterTypeAndPropagateChanges(analyzedInstruction, analyzedInstruction.getDestinationRegister() + 1, 184 RegisterType.getRegisterType(RegisterType.Category.LongHi, null)); 185 } else { 186 setRegisterTypeAndPropagateChanges(analyzedInstruction, analyzedInstruction.getDestinationRegister() + 1, 187 RegisterType.getRegisterType(RegisterType.Category.DoubleHi, null)); 188 } 189 } 190 191 private void setDestinationRegisterTypeAndPropagateChanges(AnalyzedInstruction analyzedInstruction, 192 RegisterType registerType) { 193 setRegisterTypeAndPropagateChanges(analyzedInstruction, analyzedInstruction.getDestinationRegister(), 194 registerType); 195 } 196 197 private void setRegisterTypeAndPropagateChanges(AnalyzedInstruction instruction, int registerNumber, 198 RegisterType registerType) { 199 200 BitSet changedInstructions = new BitSet(instructions.size()); 201 202 boolean changed = instruction.setPostRegisterType(registerNumber, registerType); 203 204 if (!changed || instruction.setsRegister(registerNumber)) { 205 return; 206 } 207 208 propagateRegisterToSuccessors(instruction, registerNumber, changedInstructions); 209 210 //using a for loop inside the while loop optimizes for the common case of the successors of an instruction 211 //occurring after the instruction. Any successors that occur prior to the instruction will be picked up on 212 //the next iteration of the while loop. 213 //this could also be done recursively, but in large methods it would likely cause very deep recursion, 214 //which would requires the user to specify a larger stack size. This isn't really a problem, but it is 215 //slightly annoying. 216 while (!changedInstructions.isEmpty()) { 217 for (int instructionIndex=changedInstructions.nextSetBit(0); 218 instructionIndex>=0; 219 instructionIndex=changedInstructions.nextSetBit(instructionIndex)) { 220 221 changedInstructions.clear(instructionIndex); 222 223 propagateRegisterToSuccessors(instructions.valueAt(instructionIndex), registerNumber, 224 changedInstructions); 225 } 226 } 227 } 228 229 private void propagateRegisterToSuccessors(AnalyzedInstruction instruction, int registerNumber, 230 BitSet changedInstructions) { 231 for (AnalyzedInstruction successor: instruction.successors) { 232 if (!successor.setsRegister(registerNumber)) { 233 RegisterType registerType = successor.getMergedRegisterTypeFromPredecessors(registerNumber); 234 235 if (successor.setPostRegisterType(registerNumber, registerType)) { 236 changedInstructions.set(successor.instructionIndex); 237 } 238 } 239 } 240 } 241 242 243 244 private void buildInstructionList() { 245 assert encodedMethod != null; 246 assert encodedMethod.codeItem != null; 247 int registerCount = encodedMethod.codeItem.getRegisterCount(); 248 249 startOfMethod = new AnalyzedInstruction(null, -1, registerCount); 250 251 Instruction[] insns = encodedMethod.codeItem.getInstructions(); 252 253 instructions = new SparseArray<AnalyzedInstruction>(insns.length); 254 255 //first, create all the instructions and populate the instructionAddresses array 256 int currentCodeAddress = 0; 257 for (int i=0; i<insns.length; i++) { 258 instructions.append(currentCodeAddress, new AnalyzedInstruction(insns[i], i, registerCount)); 259 assert instructions.indexOfKey(currentCodeAddress) == i; 260 currentCodeAddress += insns[i].getSize(currentCodeAddress); 261 } 262 263 //next, populate the exceptionHandlers array. The array item for each instruction that can throw an exception 264 //and is covered by a try block should be set to a list of the first instructions of each exception handler 265 //for the try block covering the instruction 266 CodeItem.TryItem[] tries = encodedMethod.codeItem.getTries(); 267 int triesIndex = 0; 268 CodeItem.TryItem currentTry = null; 269 AnalyzedInstruction[] currentExceptionHandlers = null; 270 AnalyzedInstruction[][] exceptionHandlers = new AnalyzedInstruction[insns.length][]; 271 272 for (int i=0; i<instructions.size(); i++) { 273 AnalyzedInstruction instruction = instructions.valueAt(i); 274 Opcode instructionOpcode = instruction.instruction.opcode; 275 276 //check if we have gone past the end of the current try 277 if (currentTry != null) { 278 if (currentTry.getStartCodeAddress() + currentTry.getTryLength() <= currentCodeAddress) { 279 currentTry = null; 280 triesIndex++; 281 } 282 } 283 284 //check if the next try is applicable yet 285 if (currentTry == null && triesIndex < tries.length) { 286 CodeItem.TryItem tryItem = tries[triesIndex]; 287 if (tryItem.getStartCodeAddress() <= currentCodeAddress) { 288 assert(tryItem.getStartCodeAddress() + tryItem.getTryLength() > currentCodeAddress); 289 290 currentTry = tryItem; 291 292 currentExceptionHandlers = buildExceptionHandlerArray(tryItem); 293 } 294 } 295 296 //if we're inside a try block, and the instruction can throw an exception, then add the exception handlers 297 //for the current instruction 298 if (currentTry != null && instructionOpcode.canThrow()) { 299 exceptionHandlers[i] = currentExceptionHandlers; 300 } 301 } 302 303 //finally, populate the successors and predecessors for each instruction 304 assert instructions.size() > 0; 305 addPredecessorSuccessor(startOfMethod, instructions.valueAt(0), exceptionHandlers); 306 startOfMethod.addSuccessor(instructions.valueAt(0)); 307 308 for (int i=0; i<instructions.size(); i++) { 309 AnalyzedInstruction instruction = instructions.valueAt(i); 310 Opcode instructionOpcode = instruction.instruction.opcode; 311 int instructionCodeAddress = getInstructionAddress(instruction); 312 313 if (instruction.instruction.opcode.canContinue()) { 314 if (i == instructions.size() - 1) { 315 throw new ValidationException("Execution can continue past the last instruction"); 316 } 317 AnalyzedInstruction nextInstruction = instructions.valueAt(i+1); 318 addPredecessorSuccessor(instruction, nextInstruction, exceptionHandlers); 319 } 320 321 if (instruction instanceof OffsetInstruction) { 322 OffsetInstruction offsetInstruction = (OffsetInstruction)instruction; 323 324 if (instructionOpcode == Opcode.PACKED_SWITCH || instructionOpcode == Opcode.SPARSE_SWITCH) { 325 MultiOffsetInstruction switchDataInstruction = 326 (MultiOffsetInstruction)instructions.get(instructionCodeAddress + 327 offsetInstruction.getTargetAddressOffset()).instruction; 328 for (int targetAddressOffset: switchDataInstruction.getTargets()) { 329 AnalyzedInstruction targetInstruction = instructions.get(instructionCodeAddress + 330 targetAddressOffset); 331 332 addPredecessorSuccessor(instruction, targetInstruction, exceptionHandlers); 333 } 334 } else { 335 int targetAddressOffset = offsetInstruction.getTargetAddressOffset(); 336 AnalyzedInstruction targetInstruction = instructions.get(instructionCodeAddress + 337 targetAddressOffset); 338 addPredecessorSuccessor(instruction, targetInstruction, exceptionHandlers); 339 } 340 } 341 } 342 } 343 344 private void addPredecessorSuccessor(AnalyzedInstruction predecessor, AnalyzedInstruction successor, 345 AnalyzedInstruction[][] exceptionHandlers) { 346 addPredecessorSuccessor(predecessor, successor, exceptionHandlers, false); 347 } 348 349 private void addPredecessorSuccessor(AnalyzedInstruction predecessor, AnalyzedInstruction successor, 350 AnalyzedInstruction[][] exceptionHandlers, boolean allowMoveException) { 351 352 if (!allowMoveException && successor.instruction.opcode == Opcode.MOVE_EXCEPTION) { 353 throw new ValidationException("Execution can pass from the " + predecessor.instruction.opcode.name + 354 " instruction at code address 0x" + Integer.toHexString(getInstructionAddress(predecessor)) + 355 " to the move-exception instruction at address 0x" + 356 Integer.toHexString(getInstructionAddress(successor))); 357 } 358 359 if (!predecessor.addSuccessor(successor)) { 360 //if predecessor already had successor as a successor, then there's nothing else to do 361 return; 362 } 363 364 successor.addPredecessor(predecessor); 365 366 //TODO: need to handle the case of monitor-exit as a special case - the exception is thrown *after* the instruction executes 367 //if the successor can throw an instruction, then we need to add the exception handlers as additional 368 //successors to the predecessor (and then apply this same logic recursively if needed) 369 AnalyzedInstruction[] exceptionHandlersForSuccessor = exceptionHandlers[successor.instructionIndex]; 370 if (exceptionHandlersForSuccessor != null) { 371 //the item for this instruction in exceptionHandlersForSuccessor should only be set if this instruction 372 //can throw an exception 373 assert predecessor.instruction.opcode.canThrow(); 374 375 for (AnalyzedInstruction exceptionHandler: exceptionHandlersForSuccessor) { 376 addPredecessorSuccessor(predecessor, exceptionHandler, exceptionHandlers, true); 377 } 378 } 379 } 380 381 private AnalyzedInstruction[] buildExceptionHandlerArray(CodeItem.TryItem tryItem) { 382 int exceptionHandlerCount = tryItem.encodedCatchHandler.handlers.length; 383 int catchAllHandler = tryItem.encodedCatchHandler.getCatchAllHandlerAddress(); 384 if (catchAllHandler != -1) { 385 exceptionHandlerCount++; 386 } 387 388 AnalyzedInstruction[] exceptionHandlers = new AnalyzedInstruction[exceptionHandlerCount]; 389 for (int i=0; i<tryItem.encodedCatchHandler.handlers.length; i++) { 390 exceptionHandlers[i] = instructions.get(tryItem.encodedCatchHandler.handlers[i].getHandlerAddress()); 391 } 392 393 if (catchAllHandler != -1) { 394 exceptionHandlers[exceptionHandlers.length - 1] = instructions.get(catchAllHandler); 395 } 396 397 return exceptionHandlers; 398 } 399 400 private boolean setDestinationRegisterTypeForInstruction(AnalyzedInstruction analyzedInstruction) { 401 Instruction instruction = analyzedInstruction.instruction; 402 403 switch (instruction.opcode) { 404 case NOP: 405 return true; 406 case MOVE: 407 case MOVE_FROM16: 408 case MOVE_16: 409 return handleMove(analyzedInstruction, Primitive32BitCategories); 410 case MOVE_WIDE: 411 case MOVE_WIDE_FROM16: 412 case MOVE_WIDE_16: 413 return handleMoveWide(analyzedInstruction); 414 case MOVE_OBJECT: 415 case MOVE_OBJECT_FROM16: 416 case MOVE_OBJECT_16: 417 return handleMove(analyzedInstruction, ReferenceCategories); 418 case MOVE_RESULT: 419 return handleMoveResult(analyzedInstruction, Primitive32BitCategories); 420 case MOVE_RESULT_WIDE: 421 return handleMoveResult(analyzedInstruction, WideLowCategories); 422 case MOVE_RESULT_OBJECT: 423 return handleMoveResult(analyzedInstruction, ReferenceCategories); 424 case MOVE_EXCEPTION: 425 return handleMoveException(analyzedInstruction); 426 case RETURN_VOID: 427 return handleReturnVoid(analyzedInstruction); 428 case RETURN: 429 return handleReturn(analyzedInstruction); 430 case RETURN_WIDE: 431 return handleReturnWide(analyzedInstruction); 432 case RETURN_OBJECT: 433 return handleReturnObject(analyzedInstruction); 434 case CONST_4: 435 case CONST_16: 436 case CONST: 437 return handleConst(analyzedInstruction); 438 case CONST_HIGH16: 439 return handleConstHigh16(analyzedInstruction); 440 case CONST_WIDE_16: 441 case CONST_WIDE_32: 442 case CONST_WIDE: 443 case CONST_WIDE_HIGH16: 444 return handleWideConst(analyzedInstruction); 445 case CONST_STRING: 446 case CONST_STRING_JUMBO: 447 return handleConstString(analyzedInstruction); 448 case CONST_CLASS: 449 return handleConstClass(analyzedInstruction); 450 case MONITOR_ENTER: 451 case MONITOR_EXIT: 452 return handleMonitor(analyzedInstruction); 453 case CHECK_CAST: 454 return handleCheckCast(analyzedInstruction); 455 case INSTANCE_OF: 456 return handleInstanceOf(analyzedInstruction); 457 case ARRAY_LENGTH: 458 return handleArrayLength(analyzedInstruction); 459 case NEW_INSTANCE: 460 return handleNewInstance(analyzedInstruction); 461 case NEW_ARRAY: 462 return handleNewArray(analyzedInstruction); 463 case FILLED_NEW_ARRAY: 464 return handleFilledNewArray(analyzedInstruction); 465 case FILLED_NEW_ARRAY_RANGE: 466 return handleFilledNewArrayRange(analyzedInstruction); 467 case FILL_ARRAY_DATA: 468 return handleFillArrayData(analyzedInstruction); 469 case THROW: 470 return handleThrow(analyzedInstruction); 471 case GOTO: 472 case GOTO_16: 473 case GOTO_32: 474 //nothing to do 475 return true; 476 case PACKED_SWITCH: 477 return handleSwitch(analyzedInstruction, Format.PackedSwitchData); 478 case SPARSE_SWITCH: 479 return handleSwitch(analyzedInstruction, Format.SparseSwitchData); 480 case CMPL_FLOAT: 481 case CMPG_FLOAT: 482 return handleFloatCmp(analyzedInstruction); 483 case CMPL_DOUBLE: 484 case CMPG_DOUBLE: 485 case CMP_LONG: 486 return handleWideCmp(analyzedInstruction); 487 } 488 assert false; 489 return false; 490 } 491 492 private static final EnumSet<RegisterType.Category> Primitive32BitCategories = EnumSet.of( 493 RegisterType.Category.Null, 494 RegisterType.Category.Boolean, 495 RegisterType.Category.Byte, 496 RegisterType.Category.Short, 497 RegisterType.Category.Char, 498 RegisterType.Category.Integer, 499 RegisterType.Category.Float); 500 501 private static final EnumSet<RegisterType.Category> WideLowCategories = EnumSet.of( 502 RegisterType.Category.LongLo, 503 RegisterType.Category.DoubleLo); 504 505 private static final EnumSet<RegisterType.Category> WideHighCategories = EnumSet.of( 506 RegisterType.Category.LongHi, 507 RegisterType.Category.DoubleHi); 508 509 private static final EnumSet<RegisterType.Category> ReferenceCategories = EnumSet.of( 510 RegisterType.Category.Null, 511 RegisterType.Category.Reference); 512 513 private boolean handleMove(AnalyzedInstruction analyzedInstruction, 514 EnumSet<RegisterType.Category> allowedCategories) { 515 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; 516 517 //get the "pre-instruction" register type for the source register 518 RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); 519 assert sourceRegisterType != null; 520 521 if (sourceRegisterType.category == RegisterType.Category.Unknown) { 522 //we don't know the source register type yet, so we can't verify it. Return false, and we'll come back later 523 return false; 524 } 525 526 checkRegister(sourceRegisterType, allowedCategories); 527 528 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, sourceRegisterType); 529 return true; 530 } 531 532 private boolean handleMoveWide(AnalyzedInstruction analyzedInstruction) { 533 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; 534 535 RegisterType sourceRegisterType = getAndCheckWideSourcePair(analyzedInstruction, 536 instruction.getRegisterB()); 537 assert sourceRegisterType != null; 538 539 if (sourceRegisterType.category == RegisterType.Category.Unknown) { 540 //we don't know the source register type yet, so we can't verify it. Return false, and we'll come back later 541 return false; 542 } 543 544 checkWideDestinationPair(analyzedInstruction); 545 546 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, sourceRegisterType); 547 return true; 548 } 549 550 private boolean handleMoveResult(AnalyzedInstruction analyzedInstruction, 551 EnumSet<RegisterType.Category> allowedCategories) { 552 553 //TODO: handle the case when the previous instruction is an odexed instruction 554 555 if (analyzedInstruction.instructionIndex == 0) { 556 throw new ValidationException(analyzedInstruction.instruction.opcode.name + " cannot be the first " + 557 "instruction in a method. It must occur after an invoke-*/fill-new-array instruction"); 558 } 559 560 AnalyzedInstruction previousInstruction = instructions.valueAt(analyzedInstruction.instructionIndex-1); 561 562 if (!previousInstruction.instruction.opcode.setsResult()) { 563 throw new ValidationException(analyzedInstruction.instruction.opcode.name + " must occur after an " + 564 "invoke-*/fill-new-array instruction"); 565 } 566 567 if (analyzedInstruction.instruction.opcode.setsWideRegister()) { 568 checkWideDestinationPair(analyzedInstruction); 569 } 570 571 //TODO: does dalvik allow a move-result after an invoke with a void return type? 572 RegisterType destinationRegisterType; 573 574 InstructionWithReference invokeInstruction = (InstructionWithReference)previousInstruction.instruction; 575 Item item = invokeInstruction.getReferencedItem(); 576 577 if (item instanceof MethodIdItem) { 578 destinationRegisterType = RegisterType.getRegisterTypeForTypeIdItem( 579 ((MethodIdItem)item).getPrototype().getReturnType()); 580 } else { 581 assert item instanceof TypeIdItem; 582 destinationRegisterType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item); 583 } 584 585 checkRegister(destinationRegisterType, allowedCategories); 586 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, destinationRegisterType); 587 return true; 588 } 589 590 private boolean handleMoveException(AnalyzedInstruction analyzedInstruction) { 591 CodeItem.TryItem[] tries = encodedMethod.codeItem.getTries(); 592 int instructionAddress = getInstructionAddress(analyzedInstruction); 593 594 if (tries == null) { 595 throw new ValidationException("move-exception must be the first instruction in an exception handler block"); 596 } 597 598 RegisterType exceptionType = null; 599 600 for (CodeItem.TryItem tryItem: encodedMethod.codeItem.getTries()) { 601 if (tryItem.encodedCatchHandler.getCatchAllHandlerAddress() == instructionAddress) { 602 exceptionType = RegisterType.getRegisterType(RegisterType.Category.Reference, 603 ClassPath.getClassDef("Ljava/lang/Throwable;")); 604 break; 605 } 606 for (CodeItem.EncodedTypeAddrPair handler: tryItem.encodedCatchHandler.handlers) { 607 if (handler.getHandlerAddress() == instructionAddress) { 608 exceptionType = RegisterType.getRegisterTypeForTypeIdItem(handler.exceptionType) 609 .merge(exceptionType); 610 } 611 } 612 } 613 614 //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) 615 checkRegister(exceptionType, ReferenceCategories); 616 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, exceptionType); 617 return true; 618 } 619 620 private boolean checkConstructorReturn(AnalyzedInstruction analyzedInstruction) { 621 assert this.isInstanceConstructor(); 622 623 //if we're in an instance constructor (an <init> method), then the superclass <init> must have been called. 624 //When execution enters the method, the "this" register is set as an uninitialized reference to the containing 625 //class. Once the superclass' <init> is called, the "this" register is upgraded to a full-blown reference type, 626 //so we need to ensure that the "this" register isn't an uninitialized reference 627 628 int thisRegister = getThisRegister(); 629 RegisterType thisRegisterType = analyzedInstruction.postRegisterMap[thisRegister]; 630 631 if (thisRegisterType.category == RegisterType.Category.Unknown) { 632 //we don't have enough information yet, so return false. We'll come back later 633 return false; 634 } 635 if (thisRegisterType.category == RegisterType.Category.UninitRef) { 636 throw new ValidationException("Returning from constructor without calling the superclass' <init>"); 637 } 638 assert thisRegisterType.category == RegisterType.Category.Reference; 639 assert thisRegisterType.type == ClassPath.getClassDef(encodedMethod.method.getContainingClass()); 640 return true; 641 } 642 643 private boolean handleReturnVoid(AnalyzedInstruction analyzedInstruction) { 644 if (this.isInstanceConstructor()) { 645 if (!checkConstructorReturn(analyzedInstruction)) { 646 return false; 647 } 648 } 649 650 TypeIdItem returnType = encodedMethod.method.getPrototype().getReturnType(); 651 if (returnType.getTypeDescriptor().charAt(0) != 'V') { 652 //TODO: could add which return-* variation should be used instead 653 throw new ValidationException("Cannot use return-void with a non-void return type (" + 654 returnType.getTypeDescriptor() + ")"); 655 } 656 return true; 657 } 658 659 private boolean handleReturn(AnalyzedInstruction analyzedInstruction) { 660 if (this.isInstanceConstructor()) { 661 if (!checkConstructorReturn(analyzedInstruction)) { 662 return false; 663 } 664 } 665 666 SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; 667 RegisterType returnRegisterType = analyzedInstruction.postRegisterMap[instruction.getRegisterA()]; 668 669 if (returnRegisterType.category == RegisterType.Category.Unknown) { 670 return false; 671 } 672 673 checkRegister(returnRegisterType, Primitive32BitCategories); 674 675 TypeIdItem returnType = encodedMethod.method.getPrototype().getReturnType(); 676 if (returnType.getTypeDescriptor().charAt(0) == 'V') { 677 throw new ValidationException("Cannot use return with a void return type. Use return-void instead"); 678 } 679 680 RegisterType registerType = RegisterType.getRegisterTypeForTypeIdItem(returnType); 681 682 if (!Primitive32BitCategories.contains(registerType.category)) { 683 //TODO: could add which return-* variation should be used instead 684 throw new ValidationException("Cannot use return with return type " + returnType.getTypeDescriptor()); 685 } 686 687 688 return true; 689 } 690 691 private boolean handleReturnWide(AnalyzedInstruction analyzedInstruction) { 692 if (this.isInstanceConstructor()) { 693 if (!checkConstructorReturn(analyzedInstruction)) { 694 return false; 695 } 696 } 697 698 SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; 699 RegisterType returnType = getAndCheckWideSourcePair(analyzedInstruction, instruction.getRegisterA()); 700 701 if (returnType.category == RegisterType.Category.Unknown) { 702 return false; 703 } 704 705 706 TypeIdItem returnTypeIdItem = encodedMethod.method.getPrototype().getReturnType(); 707 if (returnTypeIdItem.getTypeDescriptor().charAt(0) == 'V') { 708 throw new ValidationException("Cannot use return-wide with a void return type. Use return-void instead"); 709 } 710 711 returnType = RegisterType.getRegisterTypeForTypeIdItem(returnTypeIdItem); 712 if (!WideLowCategories.contains(returnType.category)) { 713 //TODO: could add which return-* variation should be used instead 714 throw new ValidationException("Cannot use return-wide with return type " + 715 returnTypeIdItem.getTypeDescriptor()); 716 } 717 718 return true; 719 } 720 721 private boolean handleReturnObject(AnalyzedInstruction analyzedInstruction) { 722 if (this.isInstanceConstructor()) { 723 if (!checkConstructorReturn(analyzedInstruction)) { 724 return false; 725 } 726 } 727 728 SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; 729 int returnRegister = instruction.getRegisterA(); 730 RegisterType returnRegisterType = analyzedInstruction.postRegisterMap[returnRegister]; 731 732 if (returnRegisterType.category == RegisterType.Category.Unknown) { 733 return false; 734 } 735 736 checkRegister(returnRegisterType, ReferenceCategories); 737 738 739 TypeIdItem returnTypeIdItem = encodedMethod.method.getPrototype().getReturnType(); 740 if (returnTypeIdItem.getTypeDescriptor().charAt(0) == 'V') { 741 throw new ValidationException("Cannot use return with a void return type. Use return-void instead"); 742 } 743 744 RegisterType returnType = RegisterType.getRegisterTypeForTypeIdItem(returnTypeIdItem); 745 746 if (!ReferenceCategories.contains(returnType.category)) { 747 //TODO: could add which return-* variation should be used instead 748 throw new ValidationException("Cannot use " + analyzedInstruction + " with return type " + 749 returnTypeIdItem.getTypeDescriptor()); 750 } 751 752 if (returnType.type.isInterface()) { 753 if (!returnRegisterType.type.implementsInterface(returnType.type)) { 754 //TODO: how to handle warnings? 755 } 756 } else { 757 if (!returnRegisterType.type.extendsClass(returnType.type)) { 758 throw new ValidationException("The return value in register v" + Integer.toString(returnRegister) + 759 "(" + returnRegisterType.type.getClassType() + ") is not compatible with the method's return " + 760 "type (" + returnType.type.getClassType() + ")"); 761 } 762 } 763 764 return true; 765 } 766 767 private boolean handleConst(AnalyzedInstruction analyzedInstruction) { 768 LiteralInstruction instruction = (LiteralInstruction)analyzedInstruction.instruction; 769 770 RegisterType newDestinationRegisterType = RegisterType.getRegisterTypeForLiteral(instruction.getLiteral()); 771 772 //we assume that the literal value is a valid value for the given instruction type, because it's impossible 773 //to store an invalid literal with the instruction. so we don't need to check the type of the literal 774 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, newDestinationRegisterType); 775 return true; 776 } 777 778 private boolean handleConstHigh16(AnalyzedInstruction analyzedInstruction) { 779 LiteralInstruction instruction = (LiteralInstruction)analyzedInstruction.instruction; 780 781 //TODO: test this 782 long literalValue = instruction.getLiteral() << 16; 783 RegisterType newDestinationRegisterType = RegisterType.getRegisterTypeForLiteral(literalValue); 784 785 //we assume that the literal value is a valid value for the given instruction type, because it's impossible 786 //to store an invalid literal with the instruction. so we don't need to check the type of the literal 787 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, newDestinationRegisterType); 788 return true; 789 } 790 791 private boolean handleWideConst(AnalyzedInstruction analyzedInstruction) { 792 setWideDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 793 RegisterType.getRegisterType(RegisterType.Category.LongLo, null)); 794 return true; 795 } 796 797 private boolean handleConstString(AnalyzedInstruction analyzedInstruction) { 798 ClassPath.ClassDef stringClassDef = ClassPath.getClassDef("Ljava/lang/String;"); 799 RegisterType stringType = RegisterType.getRegisterType(RegisterType.Category.Reference, stringClassDef); 800 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, stringType); 801 return true; 802 } 803 804 private boolean handleConstClass(AnalyzedInstruction analyzedInstruction) { 805 ClassPath.ClassDef classClassDef = ClassPath.getClassDef("Ljava/lang/Class;"); 806 RegisterType classType = RegisterType.getRegisterType(RegisterType.Category.Reference, classClassDef); 807 808 InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; 809 Item item = instruction.getReferencedItem(); 810 assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM; 811 812 //make sure the referenced class is resolvable 813 //TODO: need to check class access 814 ClassPath.ClassDef classDef = ClassPath.getClassDef((TypeIdItem)item); 815 return false; 816 } 817 818 private boolean handleMonitor(AnalyzedInstruction analyzedInstruction) { 819 SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction; 820 821 RegisterType registerType = analyzedInstruction.postRegisterMap[instruction.getRegisterA()]; 822 assert registerType != null; 823 if (registerType.category == RegisterType.Category.Unknown) { 824 return false; 825 } 826 827 checkRegister(registerType, ReferenceCategories); 828 return true; 829 } 830 831 private boolean handleCheckCast(AnalyzedInstruction analyzedInstruction) { 832 { 833 //ensure the "source" register is a reference type 834 SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; 835 836 RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA()); 837 assert registerType != null; 838 if (registerType.category == RegisterType.Category.Unknown) { 839 return false; 840 } 841 842 checkRegister(registerType, ReferenceCategories); 843 } 844 845 { 846 //resolve and verify the class that we're casting to 847 InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; 848 849 Item item = instruction.getReferencedItem(); 850 assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM; 851 852 //TODO: need to check class access 853 RegisterType newDestinationRegisterType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item); 854 try { 855 checkRegister(newDestinationRegisterType, ReferenceCategories); 856 } catch (ValidationException ex) { 857 //TODO: verify that dalvik allows a non-reference type.. 858 //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) 859 } 860 861 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, newDestinationRegisterType); 862 return true; 863 } 864 } 865 866 private boolean handleInstanceOf(AnalyzedInstruction analyzedInstruction) { 867 { 868 //ensure the register that is being checks is a reference type 869 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction; 870 871 RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); 872 assert registerType != null; 873 if (registerType.category == RegisterType.Category.Unknown) { 874 return false; 875 } 876 877 checkRegister(registerType, ReferenceCategories); 878 } 879 880 { 881 //resolve and verify the class that we're checking against 882 InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; 883 884 Item item = instruction.getReferencedItem(); 885 assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM; 886 RegisterType registerType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item); 887 checkRegister(registerType, ReferenceCategories); 888 889 //TODO: is it valid to use an array type? 890 891 //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. 892 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 893 RegisterType.getRegisterType(RegisterType.Category.Boolean, null)); 894 return true; 895 } 896 } 897 898 private boolean handleArrayLength(AnalyzedInstruction analyzedInstruction) { 899 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction; 900 901 int arrayRegisterNumber = instruction.getRegisterB(); 902 RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(arrayRegisterNumber); 903 assert arrayRegisterType != null; 904 if (arrayRegisterType.category == RegisterType.Category.Unknown) { 905 return false; 906 } 907 908 assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef; 909 910 checkRegister(arrayRegisterType, ReferenceCategories); 911 if (arrayRegisterType.type != null) { 912 if (arrayRegisterType.type.getClassType().charAt(0) != '[') { 913 throw new ValidationException("Cannot use array-length with non-array type " + 914 arrayRegisterType.type.getClassType()); 915 } 916 } 917 918 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 919 RegisterType.getRegisterType(RegisterType.Category.Integer, null)); 920 return true; 921 } 922 923 private boolean handleNewInstance(AnalyzedInstruction analyzedInstruction) { 924 InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; 925 926 Item item = instruction.getReferencedItem(); 927 assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM; 928 929 //TODO: need to check class access 930 RegisterType classType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item); 931 checkRegister(classType, ReferenceCategories); 932 if (((TypeIdItem)item).getTypeDescriptor().charAt(0) == '[') { 933 throw new ValidationException("Cannot use array type \"" + ((TypeIdItem)item).getTypeDescriptor() + 934 "\" with new-instance. Use new-array instead."); 935 } 936 937 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 938 RegisterType.getRegisterType(RegisterType.Category.UninitRef, classType.type)); 939 return true; 940 } 941 942 private boolean handleNewArray(AnalyzedInstruction analyzedInstruction) { 943 { 944 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; 945 946 int sizeRegister = instruction.getRegisterB(); 947 RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(sizeRegister); 948 assert registerType != null; 949 950 if (registerType.category == RegisterType.Category.Unknown) { 951 return false; 952 } 953 954 checkRegister(registerType, Primitive32BitCategories); 955 } 956 957 InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; 958 959 Item item = instruction.getReferencedItem(); 960 assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM; 961 962 RegisterType arrayType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item); 963 assert arrayType.type instanceof ClassPath.ArrayClassDef; 964 965 checkRegister(arrayType, ReferenceCategories); 966 if (arrayType.type.getClassType().charAt(0) != '[') { 967 throw new ValidationException("Cannot use non-array type \"" + arrayType.type.getClassType() + 968 "\" with new-array. Use new-instance instead."); 969 } 970 971 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, arrayType); 972 return true; 973 } 974 975 private static interface RegisterIterator { 976 int getRegister(); 977 boolean moveNext(); 978 } 979 980 private boolean handleFilledNewArrayCommon(AnalyzedInstruction analyzedInstruction, 981 RegisterIterator registerIterator) { 982 InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; 983 984 RegisterType arrayType; 985 RegisterType arrayImmediateElementType; 986 987 Item item = instruction.getReferencedItem(); 988 assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM; 989 990 ClassPath.ClassDef classDef = ClassPath.getClassDef((TypeIdItem)item); 991 992 if (classDef.getClassType().charAt(0) != '[') { 993 throw new ValidationException("Cannot use non-array type \"" + classDef.getClassType() + 994 "\" with new-array. Use new-instance instead."); 995 } 996 997 ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)classDef; 998 arrayType = RegisterType.getRegisterType(RegisterType.Category.Reference, classDef); 999 arrayImmediateElementType = RegisterType.getRegisterTypeForType( 1000 arrayClassDef.getImmediateElementClass().getClassType()); 1001 String baseElementType = arrayClassDef.getBaseElementClass().getClassType(); 1002 if (baseElementType.charAt(0) == 'J' || baseElementType.charAt(0) == 'D') { 1003 throw new ValidationException("Cannot use filled-new-array to create an array of wide values " + 1004 "(long or double)"); 1005 } 1006 1007 do { 1008 int register = registerIterator.getRegister(); 1009 RegisterType elementType = analyzedInstruction.getPreInstructionRegisterType(register); 1010 assert elementType != null; 1011 1012 if (elementType.category == RegisterType.Category.Unknown) { 1013 return false; 1014 } 1015 1016 if (!elementType.canBeAssignedTo(arrayImmediateElementType)) { 1017 throw new ValidationException("Register v" + Integer.toString(register) + " is of type " + 1018 elementType.toString() + " and is incompatible with the array type " + 1019 arrayType.type.getClassType()); 1020 } 1021 } while (registerIterator.moveNext()); 1022 1023 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, arrayType); 1024 return true; 1025 } 1026 1027 private boolean handleFilledNewArray(AnalyzedInstruction analyzedInstruction) { 1028 FiveRegisterInstruction instruction = (FiveRegisterInstruction)analyzedInstruction.instruction; 1029 final int registerCount = instruction.getRegCount(); 1030 final int[] registers = new int[]{instruction.getRegisterD(), instruction.getRegisterE(), 1031 instruction.getRegisterF(), instruction.getRegisterG(), 1032 instruction.getRegisterA()}; 1033 1034 return handleFilledNewArrayCommon(analyzedInstruction, 1035 new RegisterIterator() { 1036 private int currentRegister = 0; 1037 public int getRegister() { 1038 return registers[currentRegister]; 1039 } 1040 1041 public boolean moveNext() { 1042 currentRegister++; 1043 if (currentRegister >= registerCount) { 1044 return false; 1045 } 1046 return true; 1047 } 1048 }); 1049 } 1050 1051 private boolean handleFilledNewArrayRange(AnalyzedInstruction analyzedInstruction) { 1052 final RegisterRangeInstruction instruction = (RegisterRangeInstruction)analyzedInstruction.instruction; 1053 1054 //instruction.getStartRegister() and instruction.getRegCount() both return an int value, but are actually 1055 //unsigned 16 bit values, so we don't have to worry about overflowing an int when adding them together 1056 if (instruction.getStartRegister() + instruction.getRegCount() >= 1<<16) { 1057 throw new ValidationException(String.format("Invalid register range {v%d .. v%d}. The ending register " + 1058 "is larger than the largest allowed register of v65535.", 1059 instruction.getStartRegister(), 1060 instruction.getStartRegister() + instruction.getRegCount() - 1)); 1061 } 1062 1063 return handleFilledNewArrayCommon(analyzedInstruction, 1064 new RegisterIterator() { 1065 private int currentRegister = 0; 1066 private final int startRegister = instruction.getStartRegister(); 1067 private final int registerCount = instruction.getRegCount(); 1068 1069 public int getRegister() { 1070 return startRegister + currentRegister; 1071 } 1072 1073 public boolean moveNext() { 1074 currentRegister++; 1075 if (currentRegister >= registerCount) { 1076 return false; 1077 } 1078 return true; 1079 } 1080 }); 1081 } 1082 1083 private boolean handleFillArrayData(AnalyzedInstruction analyzedInstruction) { 1084 SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; 1085 1086 int register = instruction.getRegisterA(); 1087 RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(register); 1088 assert registerType != null; 1089 1090 if (registerType.category == RegisterType.Category.Unknown || 1091 registerType.category == RegisterType.Category.Null) { 1092 return false; 1093 } 1094 1095 if (registerType.category != RegisterType.Category.Reference) { 1096 throw new ValidationException(String.format("Cannot use fill-array-data with non-array register v%d of " + 1097 "type %s", register, registerType.toString())); 1098 } 1099 1100 assert registerType.type instanceof ClassPath.ArrayClassDef; 1101 ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)registerType.type; 1102 1103 if (arrayClassDef.getArrayDimensions() != 1) { 1104 throw new ValidationException(String.format("Cannot use fill-array-data with array type %s. It can only " + 1105 "be used with a one-dimensional array of primitives.", arrayClassDef.getClassType())); 1106 } 1107 1108 int elementWidth; 1109 switch (arrayClassDef.getBaseElementClass().getClassType().charAt(0)) { 1110 case 'Z': 1111 case 'B': 1112 elementWidth = 1; 1113 break; 1114 case 'C': 1115 case 'S': 1116 elementWidth = 2; 1117 break; 1118 case 'I': 1119 case 'F': 1120 elementWidth = 4; 1121 break; 1122 case 'J': 1123 case 'D': 1124 elementWidth = 8; 1125 break; 1126 default: 1127 throw new ValidationException(String.format("Cannot use fill-array-data with array type %s. It can " + 1128 "only be used with a one-dimensional array of primitives.", arrayClassDef.getClassType())); 1129 } 1130 1131 1132 int arrayDataAddressOffset = ((OffsetInstruction)analyzedInstruction.instruction).getTargetAddressOffset(); 1133 int arrayDataCodeAddress = getInstructionAddress(analyzedInstruction) + arrayDataAddressOffset; 1134 AnalyzedInstruction arrayDataInstruction = this.instructions.get(arrayDataCodeAddress); 1135 if (arrayDataInstruction == null || arrayDataInstruction.instruction.getFormat() != Format.ArrayData) { 1136 throw new ValidationException(String.format("Could not find an array data structure at code address 0x%x", 1137 arrayDataCodeAddress)); 1138 } 1139 1140 ArrayDataPseudoInstruction arrayDataPseudoInstruction = 1141 (ArrayDataPseudoInstruction)arrayDataInstruction.instruction; 1142 1143 if (elementWidth != arrayDataPseudoInstruction.getElementWidth()) { 1144 throw new ValidationException(String.format("The array data at code address 0x%x does not have the " + 1145 "correct element width for array type %s. Expecting element width %d, got element width %d.", 1146 arrayDataCodeAddress, arrayClassDef.getClassType(), elementWidth, 1147 arrayDataPseudoInstruction.getElementWidth())); 1148 } 1149 1150 return true; 1151 } 1152 1153 private boolean handleThrow(AnalyzedInstruction analyzedInstruction) { 1154 int register = ((SingleRegisterInstruction)analyzedInstruction.instruction).getRegisterA(); 1155 1156 RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(register); 1157 assert registerType != null; 1158 1159 if (registerType.category == RegisterType.Category.Unknown) { 1160 return false; 1161 } 1162 1163 if (registerType.category == RegisterType.Category.Null) { 1164 return true; 1165 } 1166 1167 if (registerType.category != RegisterType.Category.Reference) { 1168 throw new ValidationException(String.format("Cannot use throw with non-reference type %s in register v%d", 1169 registerType.toString(), register)); 1170 } 1171 1172 assert registerType.type != null; 1173 1174 if (!registerType.type.extendsClass(ClassPath.getClassDef("Ljava/lang/Throwable;"))) { 1175 throw new ValidationException(String.format("Cannot use throw with non-throwable type %s in register v%d", 1176 registerType.type.getClassType(), register)); 1177 } 1178 1179 return true; 1180 } 1181 1182 private boolean handleSwitch(AnalyzedInstruction analyzedInstruction, Format expectedSwitchDataFormat) { 1183 int register = ((SingleRegisterInstruction)analyzedInstruction.instruction).getRegisterA(); 1184 int switchCodeAddressOffset = ((OffsetInstruction)analyzedInstruction.instruction).getTargetAddressOffset(); 1185 1186 RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(register); 1187 assert registerType != null; 1188 1189 if (registerType.category == RegisterType.Category.Unknown) { 1190 return false; 1191 } 1192 1193 checkRegister(registerType, Primitive32BitCategories); 1194 1195 int switchDataCodeAddress = this.getInstructionAddress(analyzedInstruction) + switchCodeAddressOffset; 1196 AnalyzedInstruction switchDataAnalyzedInstruction = instructions.get(switchDataCodeAddress); 1197 1198 if (switchDataAnalyzedInstruction == null || 1199 switchDataAnalyzedInstruction.instruction.getFormat() != expectedSwitchDataFormat) { 1200 throw new ValidationException(String.format("There is no %s structure at code address 0x%x", 1201 expectedSwitchDataFormat.name(), switchDataCodeAddress)); 1202 } 1203 1204 return true; 1205 } 1206 1207 private boolean handleFloatCmp(AnalyzedInstruction analyzedInstruction) { 1208 ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; 1209 1210 RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); 1211 assert registerType != null; 1212 1213 if (registerType.category == RegisterType.Category.Unknown) { 1214 return false; 1215 } 1216 checkRegister(registerType, Primitive32BitCategories); 1217 1218 registerType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterC()); 1219 assert registerType != null; 1220 1221 if (registerType.category == RegisterType.Category.Unknown) { 1222 return false; 1223 } 1224 checkRegister(registerType, Primitive32BitCategories); 1225 1226 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 1227 RegisterType.getRegisterType(RegisterType.Category.Byte, null)); 1228 return true; 1229 } 1230 1231 private boolean handleWideCmp(AnalyzedInstruction analyzedInstruction) { 1232 ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; 1233 1234 RegisterType registerType = getAndCheckWideSourcePair(analyzedInstruction, instruction.getRegisterB()); 1235 assert registerType != null; 1236 if (registerType.category == RegisterType.Category.Unknown) { 1237 return false; 1238 } 1239 1240 registerType = getAndCheckWideSourcePair(analyzedInstruction, instruction.getRegisterC()); 1241 assert registerType != null; 1242 if (registerType.category == RegisterType.Category.Unknown) { 1243 return false; 1244 } 1245 1246 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 1247 RegisterType.getRegisterType(RegisterType.Category.Byte, null)); 1248 return true; 1249 } 1250 1251 private static void checkRegister(RegisterType registerType, EnumSet validCategories) { 1252 if (!validCategories.contains(registerType.category)) { 1253 //TODO: add expected categories to error message 1254 throw new ValidationException("Invalid register type. Expecting one of: " + " but got \"" + 1255 registerType.category + "\""); 1256 } 1257 } 1258 1259 private static void checkWideDestinationPair(AnalyzedInstruction analyzedInstruction) { 1260 int register = analyzedInstruction.getDestinationRegister(); 1261 1262 if (register == (analyzedInstruction.postRegisterMap.length - 1)) { 1263 throw new ValidationException("v" + register + " is the last register and not a valid wide register " + 1264 "pair."); 1265 } 1266 } 1267 1268 private static RegisterType getAndCheckWideSourcePair(AnalyzedInstruction analyzedInstruction, int firstRegister) { 1269 assert firstRegister >= 0 && firstRegister < analyzedInstruction.postRegisterMap.length; 1270 1271 if (firstRegister == analyzedInstruction.postRegisterMap.length - 1) { 1272 throw new ValidationException("v" + firstRegister + " is the last register and not a valid wide register " + 1273 "pair."); 1274 } 1275 1276 RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(firstRegister); 1277 assert registerType != null; 1278 if (registerType.category == RegisterType.Category.Unknown) { 1279 return registerType; 1280 } 1281 checkRegister(registerType, WideLowCategories); 1282 1283 RegisterType secondRegisterType = analyzedInstruction.getPreInstructionRegisterType(firstRegister + 1); 1284 assert secondRegisterType != null; 1285 checkRegister(secondRegisterType, WideHighCategories); 1286 1287 if (( registerType.category == RegisterType.Category.LongLo && 1288 secondRegisterType.category == RegisterType.Category.DoubleHi) 1289 || ( registerType.category == RegisterType.Category.DoubleLo && 1290 secondRegisterType.category == RegisterType.Category.LongHi)) { 1291 assert false; 1292 throw new ValidationException("The first register in the wide register pair isn't the same type (long " + 1293 "vs. double) as the second register in the pair"); 1294 } 1295 1296 return registerType; 1297 } 1298} 1299