MethodAnalyzer.java revision 55d43e36eb862bf86ceaf9c664789ce2c4d92af8
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 case IF_EQ: 488 case IF_NE: 489 return handleIfEqNe(analyzedInstruction); 490 case IF_LT: 491 case IF_GE: 492 case IF_GT: 493 case IF_LE: 494 return handleIf(analyzedInstruction); 495 case IF_EQZ: 496 case IF_NEZ: 497 return handleIfEqzNez(analyzedInstruction); 498 case IF_LTZ: 499 case IF_GEZ: 500 case IF_GTZ: 501 case IF_LEZ: 502 return handleIfz(analyzedInstruction); 503 case AGET: 504 return handle32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Integer); 505 case AGET_BOOLEAN: 506 return handle32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Boolean); 507 case AGET_BYTE: 508 return handle32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Byte); 509 case AGET_CHAR: 510 return handle32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Char); 511 case AGET_SHORT: 512 return handle32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Short); 513 case AGET_WIDE: 514 return handleAgetWide(analyzedInstruction); 515 case AGET_OBJECT: 516 return handleAgetObject(analyzedInstruction); 517 case APUT: 518 return handle32BitPrimitiveAput(analyzedInstruction, RegisterType.Category.Integer); 519 case APUT_BOOLEAN: 520 return handle32BitPrimitiveAput(analyzedInstruction, RegisterType.Category.Boolean); 521 case APUT_BYTE: 522 return handle32BitPrimitiveAput(analyzedInstruction, RegisterType.Category.Byte); 523 case APUT_CHAR: 524 return handle32BitPrimitiveAput(analyzedInstruction, RegisterType.Category.Char); 525 case APUT_SHORT: 526 return handle32BitPrimitiveAput(analyzedInstruction, RegisterType.Category.Short); 527 case APUT_WIDE: 528 return handleAputWide(analyzedInstruction); 529 } 530 531 assert false; 532 return false; 533 } 534 535 private static final EnumSet<RegisterType.Category> Primitive32BitCategories = EnumSet.of( 536 RegisterType.Category.Null, 537 RegisterType.Category.Boolean, 538 RegisterType.Category.Byte, 539 RegisterType.Category.Short, 540 RegisterType.Category.Char, 541 RegisterType.Category.Integer, 542 RegisterType.Category.Float); 543 544 private static final EnumSet<RegisterType.Category> WideLowCategories = EnumSet.of( 545 RegisterType.Category.LongLo, 546 RegisterType.Category.DoubleLo); 547 548 private static final EnumSet<RegisterType.Category> WideHighCategories = EnumSet.of( 549 RegisterType.Category.LongHi, 550 RegisterType.Category.DoubleHi); 551 552 private static final EnumSet<RegisterType.Category> ReferenceCategories = EnumSet.of( 553 RegisterType.Category.Null, 554 RegisterType.Category.Reference); 555 556 private boolean handleMove(AnalyzedInstruction analyzedInstruction, 557 EnumSet<RegisterType.Category> allowedCategories) { 558 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; 559 560 //get the "pre-instruction" register type for the source register 561 RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); 562 assert sourceRegisterType != null; 563 564 if (sourceRegisterType.category == RegisterType.Category.Unknown) { 565 //we don't know the source register type yet, so we can't verify it. Return false, and we'll come back later 566 return false; 567 } 568 569 checkRegister(sourceRegisterType, allowedCategories); 570 571 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, sourceRegisterType); 572 return true; 573 } 574 575 private boolean handleMoveWide(AnalyzedInstruction analyzedInstruction) { 576 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; 577 578 RegisterType sourceRegisterType = getAndCheckWideSourcePair(analyzedInstruction, 579 instruction.getRegisterB()); 580 assert sourceRegisterType != null; 581 582 if (sourceRegisterType.category == RegisterType.Category.Unknown) { 583 //we don't know the source register type yet, so we can't verify it. Return false, and we'll come back later 584 return false; 585 } 586 587 checkWideDestinationPair(analyzedInstruction); 588 589 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, sourceRegisterType); 590 return true; 591 } 592 593 private boolean handleMoveResult(AnalyzedInstruction analyzedInstruction, 594 EnumSet<RegisterType.Category> allowedCategories) { 595 596 //TODO: handle the case when the previous instruction is an odexed instruction 597 598 if (analyzedInstruction.instructionIndex == 0) { 599 throw new ValidationException(analyzedInstruction.instruction.opcode.name + " cannot be the first " + 600 "instruction in a method. It must occur after an invoke-*/fill-new-array instruction"); 601 } 602 603 AnalyzedInstruction previousInstruction = instructions.valueAt(analyzedInstruction.instructionIndex-1); 604 605 if (!previousInstruction.instruction.opcode.setsResult()) { 606 throw new ValidationException(analyzedInstruction.instruction.opcode.name + " must occur after an " + 607 "invoke-*/fill-new-array instruction"); 608 } 609 610 if (analyzedInstruction.instruction.opcode.setsWideRegister()) { 611 checkWideDestinationPair(analyzedInstruction); 612 } 613 614 //TODO: does dalvik allow a move-result after an invoke with a void return type? 615 RegisterType destinationRegisterType; 616 617 InstructionWithReference invokeInstruction = (InstructionWithReference)previousInstruction.instruction; 618 Item item = invokeInstruction.getReferencedItem(); 619 620 if (item instanceof MethodIdItem) { 621 destinationRegisterType = RegisterType.getRegisterTypeForTypeIdItem( 622 ((MethodIdItem)item).getPrototype().getReturnType()); 623 } else { 624 assert item instanceof TypeIdItem; 625 destinationRegisterType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item); 626 } 627 628 checkRegister(destinationRegisterType, allowedCategories); 629 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, destinationRegisterType); 630 return true; 631 } 632 633 private boolean handleMoveException(AnalyzedInstruction analyzedInstruction) { 634 CodeItem.TryItem[] tries = encodedMethod.codeItem.getTries(); 635 int instructionAddress = getInstructionAddress(analyzedInstruction); 636 637 if (tries == null) { 638 throw new ValidationException("move-exception must be the first instruction in an exception handler block"); 639 } 640 641 RegisterType exceptionType = null; 642 643 for (CodeItem.TryItem tryItem: encodedMethod.codeItem.getTries()) { 644 if (tryItem.encodedCatchHandler.getCatchAllHandlerAddress() == instructionAddress) { 645 exceptionType = RegisterType.getRegisterType(RegisterType.Category.Reference, 646 ClassPath.getClassDef("Ljava/lang/Throwable;")); 647 break; 648 } 649 for (CodeItem.EncodedTypeAddrPair handler: tryItem.encodedCatchHandler.handlers) { 650 if (handler.getHandlerAddress() == instructionAddress) { 651 exceptionType = RegisterType.getRegisterTypeForTypeIdItem(handler.exceptionType) 652 .merge(exceptionType); 653 } 654 } 655 } 656 657 //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) 658 checkRegister(exceptionType, ReferenceCategories); 659 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, exceptionType); 660 return true; 661 } 662 663 private boolean checkConstructorReturn(AnalyzedInstruction analyzedInstruction) { 664 assert this.isInstanceConstructor(); 665 666 //if we're in an instance constructor (an <init> method), then the superclass <init> must have been called. 667 //When execution enters the method, the "this" register is set as an uninitialized reference to the containing 668 //class. Once the superclass' <init> is called, the "this" register is upgraded to a full-blown reference type, 669 //so we need to ensure that the "this" register isn't an uninitialized reference 670 671 int thisRegister = getThisRegister(); 672 RegisterType thisRegisterType = analyzedInstruction.postRegisterMap[thisRegister]; 673 674 if (thisRegisterType.category == RegisterType.Category.Unknown) { 675 //we don't have enough information yet, so return false. We'll come back later 676 return false; 677 } 678 if (thisRegisterType.category == RegisterType.Category.UninitRef) { 679 throw new ValidationException("Returning from constructor without calling the superclass' <init>"); 680 } 681 assert thisRegisterType.category == RegisterType.Category.Reference; 682 assert thisRegisterType.type == ClassPath.getClassDef(encodedMethod.method.getContainingClass()); 683 return true; 684 } 685 686 private boolean handleReturnVoid(AnalyzedInstruction analyzedInstruction) { 687 if (this.isInstanceConstructor()) { 688 if (!checkConstructorReturn(analyzedInstruction)) { 689 return false; 690 } 691 } 692 693 TypeIdItem returnType = encodedMethod.method.getPrototype().getReturnType(); 694 if (returnType.getTypeDescriptor().charAt(0) != 'V') { 695 //TODO: could add which return-* variation should be used instead 696 throw new ValidationException("Cannot use return-void with a non-void return type (" + 697 returnType.getTypeDescriptor() + ")"); 698 } 699 return true; 700 } 701 702 private boolean handleReturn(AnalyzedInstruction analyzedInstruction) { 703 if (this.isInstanceConstructor()) { 704 if (!checkConstructorReturn(analyzedInstruction)) { 705 return false; 706 } 707 } 708 709 SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; 710 RegisterType returnRegisterType = analyzedInstruction.postRegisterMap[instruction.getRegisterA()]; 711 712 if (returnRegisterType.category == RegisterType.Category.Unknown) { 713 return false; 714 } 715 716 checkRegister(returnRegisterType, Primitive32BitCategories); 717 718 TypeIdItem returnType = encodedMethod.method.getPrototype().getReturnType(); 719 if (returnType.getTypeDescriptor().charAt(0) == 'V') { 720 throw new ValidationException("Cannot use return with a void return type. Use return-void instead"); 721 } 722 723 RegisterType registerType = RegisterType.getRegisterTypeForTypeIdItem(returnType); 724 725 if (!Primitive32BitCategories.contains(registerType.category)) { 726 //TODO: could add which return-* variation should be used instead 727 throw new ValidationException("Cannot use return with return type " + returnType.getTypeDescriptor()); 728 } 729 730 731 return true; 732 } 733 734 private boolean handleReturnWide(AnalyzedInstruction analyzedInstruction) { 735 if (this.isInstanceConstructor()) { 736 if (!checkConstructorReturn(analyzedInstruction)) { 737 return false; 738 } 739 } 740 741 SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; 742 RegisterType returnType = getAndCheckWideSourcePair(analyzedInstruction, instruction.getRegisterA()); 743 744 if (returnType.category == RegisterType.Category.Unknown) { 745 return false; 746 } 747 748 749 TypeIdItem returnTypeIdItem = encodedMethod.method.getPrototype().getReturnType(); 750 if (returnTypeIdItem.getTypeDescriptor().charAt(0) == 'V') { 751 throw new ValidationException("Cannot use return-wide with a void return type. Use return-void instead"); 752 } 753 754 returnType = RegisterType.getRegisterTypeForTypeIdItem(returnTypeIdItem); 755 if (!WideLowCategories.contains(returnType.category)) { 756 //TODO: could add which return-* variation should be used instead 757 throw new ValidationException("Cannot use return-wide with return type " + 758 returnTypeIdItem.getTypeDescriptor()); 759 } 760 761 return true; 762 } 763 764 private boolean handleReturnObject(AnalyzedInstruction analyzedInstruction) { 765 if (this.isInstanceConstructor()) { 766 if (!checkConstructorReturn(analyzedInstruction)) { 767 return false; 768 } 769 } 770 771 SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; 772 int returnRegister = instruction.getRegisterA(); 773 RegisterType returnRegisterType = analyzedInstruction.postRegisterMap[returnRegister]; 774 775 if (returnRegisterType.category == RegisterType.Category.Unknown) { 776 return false; 777 } 778 779 checkRegister(returnRegisterType, ReferenceCategories); 780 781 782 TypeIdItem returnTypeIdItem = encodedMethod.method.getPrototype().getReturnType(); 783 if (returnTypeIdItem.getTypeDescriptor().charAt(0) == 'V') { 784 throw new ValidationException("Cannot use return with a void return type. Use return-void instead"); 785 } 786 787 RegisterType returnType = RegisterType.getRegisterTypeForTypeIdItem(returnTypeIdItem); 788 789 if (!ReferenceCategories.contains(returnType.category)) { 790 //TODO: could add which return-* variation should be used instead 791 throw new ValidationException("Cannot use " + analyzedInstruction + " with return type " + 792 returnTypeIdItem.getTypeDescriptor()); 793 } 794 795 if (returnType.type.isInterface()) { 796 if (!returnRegisterType.type.implementsInterface(returnType.type)) { 797 //TODO: how to handle warnings? 798 } 799 } else { 800 if (!returnRegisterType.type.extendsClass(returnType.type)) { 801 throw new ValidationException("The return value in register v" + Integer.toString(returnRegister) + 802 "(" + returnRegisterType.type.getClassType() + ") is not compatible with the method's return " + 803 "type (" + returnType.type.getClassType() + ")"); 804 } 805 } 806 807 return true; 808 } 809 810 private boolean handleConst(AnalyzedInstruction analyzedInstruction) { 811 LiteralInstruction instruction = (LiteralInstruction)analyzedInstruction.instruction; 812 813 RegisterType newDestinationRegisterType = RegisterType.getRegisterTypeForLiteral(instruction.getLiteral()); 814 815 //we assume that the literal value is a valid value for the given instruction type, because it's impossible 816 //to store an invalid literal with the instruction. so we don't need to check the type of the literal 817 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, newDestinationRegisterType); 818 return true; 819 } 820 821 private boolean handleConstHigh16(AnalyzedInstruction analyzedInstruction) { 822 LiteralInstruction instruction = (LiteralInstruction)analyzedInstruction.instruction; 823 824 //TODO: test this 825 long literalValue = instruction.getLiteral() << 16; 826 RegisterType newDestinationRegisterType = RegisterType.getRegisterTypeForLiteral(literalValue); 827 828 //we assume that the literal value is a valid value for the given instruction type, because it's impossible 829 //to store an invalid literal with the instruction. so we don't need to check the type of the literal 830 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, newDestinationRegisterType); 831 return true; 832 } 833 834 private boolean handleWideConst(AnalyzedInstruction analyzedInstruction) { 835 setWideDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 836 RegisterType.getRegisterType(RegisterType.Category.LongLo, null)); 837 return true; 838 } 839 840 private boolean handleConstString(AnalyzedInstruction analyzedInstruction) { 841 ClassPath.ClassDef stringClassDef = ClassPath.getClassDef("Ljava/lang/String;"); 842 RegisterType stringType = RegisterType.getRegisterType(RegisterType.Category.Reference, stringClassDef); 843 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, stringType); 844 return true; 845 } 846 847 private boolean handleConstClass(AnalyzedInstruction analyzedInstruction) { 848 ClassPath.ClassDef classClassDef = ClassPath.getClassDef("Ljava/lang/Class;"); 849 RegisterType classType = RegisterType.getRegisterType(RegisterType.Category.Reference, classClassDef); 850 851 InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; 852 Item item = instruction.getReferencedItem(); 853 assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM; 854 855 //make sure the referenced class is resolvable 856 //TODO: need to check class access 857 ClassPath.ClassDef classDef = ClassPath.getClassDef((TypeIdItem)item); 858 return false; 859 } 860 861 private boolean handleMonitor(AnalyzedInstruction analyzedInstruction) { 862 SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction; 863 864 RegisterType registerType = analyzedInstruction.postRegisterMap[instruction.getRegisterA()]; 865 assert registerType != null; 866 if (registerType.category == RegisterType.Category.Unknown) { 867 return false; 868 } 869 870 checkRegister(registerType, ReferenceCategories); 871 return true; 872 } 873 874 private boolean handleCheckCast(AnalyzedInstruction analyzedInstruction) { 875 { 876 //ensure the "source" register is a reference type 877 SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; 878 879 RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA()); 880 assert registerType != null; 881 if (registerType.category == RegisterType.Category.Unknown) { 882 return false; 883 } 884 885 checkRegister(registerType, ReferenceCategories); 886 } 887 888 { 889 //resolve and verify the class that we're casting to 890 InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; 891 892 Item item = instruction.getReferencedItem(); 893 assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM; 894 895 //TODO: need to check class access 896 RegisterType newDestinationRegisterType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item); 897 try { 898 checkRegister(newDestinationRegisterType, ReferenceCategories); 899 } catch (ValidationException ex) { 900 //TODO: verify that dalvik allows a non-reference type.. 901 //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) 902 } 903 904 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, newDestinationRegisterType); 905 return true; 906 } 907 } 908 909 private boolean handleInstanceOf(AnalyzedInstruction analyzedInstruction) { 910 { 911 //ensure the register that is being checks is a reference type 912 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction; 913 914 RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); 915 assert registerType != null; 916 if (registerType.category == RegisterType.Category.Unknown) { 917 return false; 918 } 919 920 checkRegister(registerType, ReferenceCategories); 921 } 922 923 { 924 //resolve and verify the class that we're checking against 925 InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; 926 927 Item item = instruction.getReferencedItem(); 928 assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM; 929 RegisterType registerType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item); 930 checkRegister(registerType, ReferenceCategories); 931 932 //TODO: is it valid to use an array type? 933 934 //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. 935 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 936 RegisterType.getRegisterType(RegisterType.Category.Boolean, null)); 937 return true; 938 } 939 } 940 941 private boolean handleArrayLength(AnalyzedInstruction analyzedInstruction) { 942 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction; 943 944 int arrayRegisterNumber = instruction.getRegisterB(); 945 RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(arrayRegisterNumber); 946 assert arrayRegisterType != null; 947 if (arrayRegisterType.category == RegisterType.Category.Unknown) { 948 return false; 949 } 950 951 assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef; 952 953 checkRegister(arrayRegisterType, ReferenceCategories); 954 if (arrayRegisterType.type != null) { 955 if (arrayRegisterType.type.getClassType().charAt(0) != '[') { 956 throw new ValidationException("Cannot use array-length with non-array type " + 957 arrayRegisterType.type.getClassType()); 958 } 959 } 960 961 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 962 RegisterType.getRegisterType(RegisterType.Category.Integer, null)); 963 return true; 964 } 965 966 private boolean handleNewInstance(AnalyzedInstruction analyzedInstruction) { 967 InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; 968 969 Item item = instruction.getReferencedItem(); 970 assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM; 971 972 //TODO: need to check class access 973 RegisterType classType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item); 974 checkRegister(classType, ReferenceCategories); 975 if (((TypeIdItem)item).getTypeDescriptor().charAt(0) == '[') { 976 throw new ValidationException("Cannot use array type \"" + ((TypeIdItem)item).getTypeDescriptor() + 977 "\" with new-instance. Use new-array instead."); 978 } 979 980 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 981 RegisterType.getRegisterType(RegisterType.Category.UninitRef, classType.type)); 982 return true; 983 } 984 985 private boolean handleNewArray(AnalyzedInstruction analyzedInstruction) { 986 { 987 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; 988 989 int sizeRegister = instruction.getRegisterB(); 990 RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(sizeRegister); 991 assert registerType != null; 992 993 if (registerType.category == RegisterType.Category.Unknown) { 994 return false; 995 } 996 997 checkRegister(registerType, Primitive32BitCategories); 998 } 999 1000 InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; 1001 1002 Item item = instruction.getReferencedItem(); 1003 assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM; 1004 1005 RegisterType arrayType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item); 1006 assert arrayType.type instanceof ClassPath.ArrayClassDef; 1007 1008 checkRegister(arrayType, ReferenceCategories); 1009 if (arrayType.type.getClassType().charAt(0) != '[') { 1010 throw new ValidationException("Cannot use non-array type \"" + arrayType.type.getClassType() + 1011 "\" with new-array. Use new-instance instead."); 1012 } 1013 1014 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, arrayType); 1015 return true; 1016 } 1017 1018 private static interface RegisterIterator { 1019 int getRegister(); 1020 boolean moveNext(); 1021 } 1022 1023 private boolean handleFilledNewArrayCommon(AnalyzedInstruction analyzedInstruction, 1024 RegisterIterator registerIterator) { 1025 InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; 1026 1027 RegisterType arrayType; 1028 RegisterType arrayImmediateElementType; 1029 1030 Item item = instruction.getReferencedItem(); 1031 assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM; 1032 1033 ClassPath.ClassDef classDef = ClassPath.getClassDef((TypeIdItem)item); 1034 1035 if (classDef.getClassType().charAt(0) != '[') { 1036 throw new ValidationException("Cannot use non-array type \"" + classDef.getClassType() + 1037 "\" with new-array. Use new-instance instead."); 1038 } 1039 1040 ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)classDef; 1041 arrayType = RegisterType.getRegisterType(RegisterType.Category.Reference, classDef); 1042 arrayImmediateElementType = RegisterType.getRegisterTypeForType( 1043 arrayClassDef.getImmediateElementClass().getClassType()); 1044 String baseElementType = arrayClassDef.getBaseElementClass().getClassType(); 1045 if (baseElementType.charAt(0) == 'J' || baseElementType.charAt(0) == 'D') { 1046 throw new ValidationException("Cannot use filled-new-array to create an array of wide values " + 1047 "(long or double)"); 1048 } 1049 1050 do { 1051 int register = registerIterator.getRegister(); 1052 RegisterType elementType = analyzedInstruction.getPreInstructionRegisterType(register); 1053 assert elementType != null; 1054 1055 if (elementType.category == RegisterType.Category.Unknown) { 1056 return false; 1057 } 1058 1059 if (!elementType.canBeAssignedTo(arrayImmediateElementType)) { 1060 throw new ValidationException("Register v" + Integer.toString(register) + " is of type " + 1061 elementType.toString() + " and is incompatible with the array type " + 1062 arrayType.type.getClassType()); 1063 } 1064 } while (registerIterator.moveNext()); 1065 1066 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, arrayType); 1067 return true; 1068 } 1069 1070 private boolean handleFilledNewArray(AnalyzedInstruction analyzedInstruction) { 1071 FiveRegisterInstruction instruction = (FiveRegisterInstruction)analyzedInstruction.instruction; 1072 final int registerCount = instruction.getRegCount(); 1073 final int[] registers = new int[]{instruction.getRegisterD(), instruction.getRegisterE(), 1074 instruction.getRegisterF(), instruction.getRegisterG(), 1075 instruction.getRegisterA()}; 1076 1077 return handleFilledNewArrayCommon(analyzedInstruction, 1078 new RegisterIterator() { 1079 private int currentRegister = 0; 1080 public int getRegister() { 1081 return registers[currentRegister]; 1082 } 1083 1084 public boolean moveNext() { 1085 currentRegister++; 1086 if (currentRegister >= registerCount) { 1087 return false; 1088 } 1089 return true; 1090 } 1091 }); 1092 } 1093 1094 private boolean handleFilledNewArrayRange(AnalyzedInstruction analyzedInstruction) { 1095 final RegisterRangeInstruction instruction = (RegisterRangeInstruction)analyzedInstruction.instruction; 1096 1097 //instruction.getStartRegister() and instruction.getRegCount() both return an int value, but are actually 1098 //unsigned 16 bit values, so we don't have to worry about overflowing an int when adding them together 1099 if (instruction.getStartRegister() + instruction.getRegCount() >= 1<<16) { 1100 throw new ValidationException(String.format("Invalid register range {v%d .. v%d}. The ending register " + 1101 "is larger than the largest allowed register of v65535.", 1102 instruction.getStartRegister(), 1103 instruction.getStartRegister() + instruction.getRegCount() - 1)); 1104 } 1105 1106 return handleFilledNewArrayCommon(analyzedInstruction, 1107 new RegisterIterator() { 1108 private int currentRegister = 0; 1109 private final int startRegister = instruction.getStartRegister(); 1110 private final int registerCount = instruction.getRegCount(); 1111 1112 public int getRegister() { 1113 return startRegister + currentRegister; 1114 } 1115 1116 public boolean moveNext() { 1117 currentRegister++; 1118 if (currentRegister >= registerCount) { 1119 return false; 1120 } 1121 return true; 1122 } 1123 }); 1124 } 1125 1126 private boolean handleFillArrayData(AnalyzedInstruction analyzedInstruction) { 1127 SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; 1128 1129 int register = instruction.getRegisterA(); 1130 RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(register); 1131 assert registerType != null; 1132 1133 if (registerType.category == RegisterType.Category.Unknown || 1134 registerType.category == RegisterType.Category.Null) { 1135 return false; 1136 } 1137 1138 if (registerType.category != RegisterType.Category.Reference) { 1139 throw new ValidationException(String.format("Cannot use fill-array-data with non-array register v%d of " + 1140 "type %s", register, registerType.toString())); 1141 } 1142 1143 assert registerType.type instanceof ClassPath.ArrayClassDef; 1144 ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)registerType.type; 1145 1146 if (arrayClassDef.getArrayDimensions() != 1) { 1147 throw new ValidationException(String.format("Cannot use fill-array-data with array type %s. It can only " + 1148 "be used with a one-dimensional array of primitives.", arrayClassDef.getClassType())); 1149 } 1150 1151 int elementWidth; 1152 switch (arrayClassDef.getBaseElementClass().getClassType().charAt(0)) { 1153 case 'Z': 1154 case 'B': 1155 elementWidth = 1; 1156 break; 1157 case 'C': 1158 case 'S': 1159 elementWidth = 2; 1160 break; 1161 case 'I': 1162 case 'F': 1163 elementWidth = 4; 1164 break; 1165 case 'J': 1166 case 'D': 1167 elementWidth = 8; 1168 break; 1169 default: 1170 throw new ValidationException(String.format("Cannot use fill-array-data with array type %s. It can " + 1171 "only be used with a one-dimensional array of primitives.", arrayClassDef.getClassType())); 1172 } 1173 1174 1175 int arrayDataAddressOffset = ((OffsetInstruction)analyzedInstruction.instruction).getTargetAddressOffset(); 1176 int arrayDataCodeAddress = getInstructionAddress(analyzedInstruction) + arrayDataAddressOffset; 1177 AnalyzedInstruction arrayDataInstruction = this.instructions.get(arrayDataCodeAddress); 1178 if (arrayDataInstruction == null || arrayDataInstruction.instruction.getFormat() != Format.ArrayData) { 1179 throw new ValidationException(String.format("Could not find an array data structure at code address 0x%x", 1180 arrayDataCodeAddress)); 1181 } 1182 1183 ArrayDataPseudoInstruction arrayDataPseudoInstruction = 1184 (ArrayDataPseudoInstruction)arrayDataInstruction.instruction; 1185 1186 if (elementWidth != arrayDataPseudoInstruction.getElementWidth()) { 1187 throw new ValidationException(String.format("The array data at code address 0x%x does not have the " + 1188 "correct element width for array type %s. Expecting element width %d, got element width %d.", 1189 arrayDataCodeAddress, arrayClassDef.getClassType(), elementWidth, 1190 arrayDataPseudoInstruction.getElementWidth())); 1191 } 1192 1193 return true; 1194 } 1195 1196 private boolean handleThrow(AnalyzedInstruction analyzedInstruction) { 1197 int register = ((SingleRegisterInstruction)analyzedInstruction.instruction).getRegisterA(); 1198 1199 RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(register); 1200 assert registerType != null; 1201 1202 if (registerType.category == RegisterType.Category.Unknown) { 1203 return false; 1204 } 1205 1206 if (registerType.category == RegisterType.Category.Null) { 1207 return true; 1208 } 1209 1210 if (registerType.category != RegisterType.Category.Reference) { 1211 throw new ValidationException(String.format("Cannot use throw with non-reference type %s in register v%d", 1212 registerType.toString(), register)); 1213 } 1214 1215 assert registerType.type != null; 1216 1217 if (!registerType.type.extendsClass(ClassPath.getClassDef("Ljava/lang/Throwable;"))) { 1218 throw new ValidationException(String.format("Cannot use throw with non-throwable type %s in register v%d", 1219 registerType.type.getClassType(), register)); 1220 } 1221 1222 return true; 1223 } 1224 1225 private boolean handleSwitch(AnalyzedInstruction analyzedInstruction, Format expectedSwitchDataFormat) { 1226 int register = ((SingleRegisterInstruction)analyzedInstruction.instruction).getRegisterA(); 1227 int switchCodeAddressOffset = ((OffsetInstruction)analyzedInstruction.instruction).getTargetAddressOffset(); 1228 1229 RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(register); 1230 assert registerType != null; 1231 1232 if (registerType.category == RegisterType.Category.Unknown) { 1233 return false; 1234 } 1235 1236 checkRegister(registerType, Primitive32BitCategories); 1237 1238 int switchDataCodeAddress = this.getInstructionAddress(analyzedInstruction) + switchCodeAddressOffset; 1239 AnalyzedInstruction switchDataAnalyzedInstruction = instructions.get(switchDataCodeAddress); 1240 1241 if (switchDataAnalyzedInstruction == null || 1242 switchDataAnalyzedInstruction.instruction.getFormat() != expectedSwitchDataFormat) { 1243 throw new ValidationException(String.format("There is no %s structure at code address 0x%x", 1244 expectedSwitchDataFormat.name(), switchDataCodeAddress)); 1245 } 1246 1247 return true; 1248 } 1249 1250 private boolean handleFloatCmp(AnalyzedInstruction analyzedInstruction) { 1251 ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; 1252 1253 RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); 1254 assert registerType != null; 1255 1256 if (registerType.category == RegisterType.Category.Unknown) { 1257 return false; 1258 } 1259 checkRegister(registerType, Primitive32BitCategories); 1260 1261 registerType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterC()); 1262 assert registerType != null; 1263 1264 if (registerType.category == RegisterType.Category.Unknown) { 1265 return false; 1266 } 1267 checkRegister(registerType, Primitive32BitCategories); 1268 1269 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 1270 RegisterType.getRegisterType(RegisterType.Category.Byte, null)); 1271 return true; 1272 } 1273 1274 private boolean handleWideCmp(AnalyzedInstruction analyzedInstruction) { 1275 ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; 1276 1277 RegisterType registerType = getAndCheckWideSourcePair(analyzedInstruction, instruction.getRegisterB()); 1278 assert registerType != null; 1279 if (registerType.category == RegisterType.Category.Unknown) { 1280 return false; 1281 } 1282 1283 registerType = getAndCheckWideSourcePair(analyzedInstruction, instruction.getRegisterC()); 1284 assert registerType != null; 1285 if (registerType.category == RegisterType.Category.Unknown) { 1286 return false; 1287 } 1288 1289 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 1290 RegisterType.getRegisterType(RegisterType.Category.Byte, null)); 1291 return true; 1292 } 1293 1294 private boolean handleIfEqNe(AnalyzedInstruction analyzedInstruction) { 1295 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; 1296 1297 RegisterType registerType1 = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA()); 1298 assert registerType1 != null; 1299 if (registerType1.category == RegisterType.Category.Unknown) { 1300 return false; 1301 } 1302 1303 RegisterType registerType2 = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); 1304 assert registerType2 != null; 1305 if (registerType2.category == RegisterType.Category.Unknown) { 1306 return false; 1307 } 1308 1309 if (!( 1310 (ReferenceCategories.contains(registerType1.category) && 1311 ReferenceCategories.contains(registerType2.category)) 1312 || 1313 (Primitive32BitCategories.contains(registerType1.category) && 1314 Primitive32BitCategories.contains(registerType2.category)) 1315 )) { 1316 1317 throw new ValidationException(String.format("%s cannot be used on registers of dissimilar types %s and " + 1318 "%s. They must both be a reference type or a primitive 32 bit type.", 1319 analyzedInstruction.instruction.opcode.name, registerType1.toString(), registerType2.toString())); 1320 } 1321 1322 return true; 1323 } 1324 1325 private boolean handleIf(AnalyzedInstruction analyzedInstruction) { 1326 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; 1327 1328 RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA()); 1329 assert registerType != null; 1330 1331 if (registerType.category == RegisterType.Category.Unknown) { 1332 return false; 1333 } 1334 checkRegister(registerType, Primitive32BitCategories); 1335 1336 registerType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); 1337 assert registerType != null; 1338 1339 if (registerType.category == RegisterType.Category.Unknown) { 1340 return false; 1341 } 1342 checkRegister(registerType, Primitive32BitCategories); 1343 1344 return true; 1345 } 1346 1347 private boolean handleIfEqzNez(AnalyzedInstruction analyzedInstruction) { 1348 SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; 1349 1350 RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA()); 1351 assert registerType != null; 1352 if (registerType.category == RegisterType.Category.Unknown) { 1353 return false; 1354 } 1355 1356 if (!ReferenceCategories.contains(registerType.category) && 1357 !Primitive32BitCategories.contains(registerType.category)) { 1358 throw new ValidationException(String.format("%s cannot be used with register type %s. Expecting 32-bit " + 1359 "primitive type or reference type.", analyzedInstruction.instruction.opcode)); 1360 } 1361 1362 return true; 1363 } 1364 1365 private boolean handleIfz(AnalyzedInstruction analyzedInstruction) { 1366 SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; 1367 1368 RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA()); 1369 assert registerType != null; 1370 1371 if (registerType.category == RegisterType.Category.Unknown) { 1372 return false; 1373 } 1374 checkRegister(registerType, Primitive32BitCategories); 1375 1376 return true; 1377 } 1378 1379 private boolean handle32BitPrimitiveAget(AnalyzedInstruction analyzedInstruction, 1380 RegisterType.Category instructionCategory) { 1381 ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; 1382 1383 RegisterType indexRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterC()); 1384 assert indexRegisterType != null; 1385 if (indexRegisterType.category == RegisterType.Category.Unknown) { 1386 return false; 1387 } 1388 checkRegister(indexRegisterType, Primitive32BitCategories); 1389 1390 RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); 1391 assert arrayRegisterType != null; 1392 if (indexRegisterType.category == RegisterType.Category.Unknown) { 1393 return false; 1394 } 1395 1396 if (arrayRegisterType.category != RegisterType.Category.Null) { 1397 if (arrayRegisterType.category != RegisterType.Category.Reference) { 1398 throw new ValidationException(String.format("Cannot use %s with non-array type %s", 1399 analyzedInstruction.instruction.opcode.name, arrayRegisterType.category.toString())); 1400 } 1401 1402 assert arrayRegisterType.type != null; 1403 if (arrayRegisterType.type.getClassType().charAt(0) != '[') { 1404 throw new ValidationException(String.format("Cannot use %s with non-array type %s", 1405 analyzedInstruction.instruction.opcode.name, arrayRegisterType.type.getClassType())); 1406 } 1407 1408 assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef; 1409 ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type; 1410 1411 if (arrayClassDef.getArrayDimensions() != 1) { 1412 throw new ValidationException(String.format("Cannot use %s with multi-dimensional array type %s", 1413 analyzedInstruction.instruction.opcode.name, arrayRegisterType.type.getClassType())); 1414 } 1415 1416 RegisterType arrayBaseType = 1417 RegisterType.getRegisterTypeForType(arrayClassDef.getBaseElementClass().getClassType()); 1418 if (checkArrayFieldAssignment(arrayBaseType.category, instructionCategory)) { 1419 throw new ValidationException(String.format("Cannot use %s with array type %s. Incorrect array type " + 1420 "for the instruction.", analyzedInstruction.instruction.opcode.name, 1421 arrayRegisterType.type.getClassType())); 1422 } 1423 } 1424 1425 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 1426 RegisterType.getRegisterType(instructionCategory, null)); 1427 1428 return true; 1429 } 1430 1431 private boolean handleAgetWide(AnalyzedInstruction analyzedInstruction) { 1432 ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; 1433 1434 RegisterType indexRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterC()); 1435 assert indexRegisterType != null; 1436 if (indexRegisterType.category == RegisterType.Category.Unknown) { 1437 return false; 1438 } 1439 checkRegister(indexRegisterType, Primitive32BitCategories); 1440 1441 RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); 1442 assert arrayRegisterType != null; 1443 if (indexRegisterType.category == RegisterType.Category.Unknown) { 1444 return false; 1445 } 1446 1447 if (arrayRegisterType.category != RegisterType.Category.Null) { 1448 if (arrayRegisterType.category != RegisterType.Category.Reference) { 1449 throw new ValidationException(String.format("Cannot use aget-wide with non-array type %s", 1450 arrayRegisterType.category.toString())); 1451 } 1452 1453 assert arrayRegisterType.type != null; 1454 if (arrayRegisterType.type.getClassType().charAt(0) != '[') { 1455 throw new ValidationException(String.format("Cannot use aget-wide with non-array type %s", 1456 arrayRegisterType.type.getClassType())); 1457 } 1458 1459 assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef; 1460 ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type; 1461 1462 if (arrayClassDef.getArrayDimensions() != 1) { 1463 throw new ValidationException(String.format("Cannot use aget-wide with multi-dimensional array type %s", 1464 arrayRegisterType.type.getClassType())); 1465 } 1466 1467 char arrayBaseType = arrayClassDef.getBaseElementClass().getClassType().charAt(0); 1468 if (arrayBaseType == 'J') { 1469 setWideDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 1470 RegisterType.getRegisterType(RegisterType.Category.LongLo, null)); 1471 } else if (arrayBaseType == 'D') { 1472 setWideDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 1473 RegisterType.getRegisterType(RegisterType.Category.DoubleLo, null)); 1474 } else { 1475 throw new ValidationException(String.format("Cannot use aget-wide with array type %s. Incorrect " + 1476 "array type for the instruction.", arrayRegisterType.type.getClassType())); 1477 } 1478 } else { 1479 setWideDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 1480 RegisterType.getRegisterType(RegisterType.Category.LongLo, null)); 1481 } 1482 1483 return true; 1484 } 1485 1486 private boolean handleAgetObject(AnalyzedInstruction analyzedInstruction) { 1487 ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; 1488 1489 RegisterType indexRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterC()); 1490 assert indexRegisterType != null; 1491 if (indexRegisterType.category == RegisterType.Category.Unknown) { 1492 return false; 1493 } 1494 checkRegister(indexRegisterType, Primitive32BitCategories); 1495 1496 RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); 1497 assert arrayRegisterType != null; 1498 if (indexRegisterType.category == RegisterType.Category.Unknown) { 1499 return false; 1500 } 1501 1502 if (arrayRegisterType.category != RegisterType.Category.Null) { 1503 if (arrayRegisterType.category != RegisterType.Category.Reference) { 1504 throw new ValidationException(String.format("Cannot use aget-object with non-array type %s", 1505 arrayRegisterType.category.toString())); 1506 } 1507 1508 assert arrayRegisterType.type != null; 1509 if (arrayRegisterType.type.getClassType().charAt(0) != '[') { 1510 throw new ValidationException(String.format("Cannot use aget-object with non-array type %s", 1511 arrayRegisterType.type.getClassType())); 1512 } 1513 1514 assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef; 1515 ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type; 1516 1517 ClassPath.ClassDef elementClassDef = arrayClassDef.getImmediateElementClass(); 1518 char elementTypePrefix = elementClassDef.getClassType().charAt(0); 1519 if (elementTypePrefix != 'L' && elementTypePrefix != '[') { 1520 throw new ValidationException(String.format("Cannot use aget-object with array type %s. Incorrect " + 1521 "array type for the instruction.", arrayRegisterType.type.getClassType())); 1522 } 1523 1524 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 1525 RegisterType.getRegisterType(RegisterType.Category.Reference, elementClassDef)); 1526 } else { 1527 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, 1528 RegisterType.getRegisterType(RegisterType.Category.Null, null)); 1529 } 1530 1531 return true; 1532 } 1533 1534 private boolean handle32BitPrimitiveAput(AnalyzedInstruction analyzedInstruction, 1535 RegisterType.Category instructionCategory) { 1536 ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; 1537 1538 RegisterType indexRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterC()); 1539 assert indexRegisterType != null; 1540 if (indexRegisterType.category == RegisterType.Category.Unknown) { 1541 return false; 1542 } 1543 checkRegister(indexRegisterType, Primitive32BitCategories); 1544 1545 1546 RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA()); 1547 assert sourceRegisterType != null; 1548 if (sourceRegisterType.category == RegisterType.Category.Unknown) { 1549 return false; 1550 } 1551 RegisterType instructionRegisterType = RegisterType.getRegisterType(instructionCategory, null); 1552 if (!sourceRegisterType.canBeAssignedTo(instructionRegisterType)) { 1553 throw new ValidationException(String.format("Cannot use %s with source register type %s.", 1554 analyzedInstruction.instruction.opcode.name, sourceRegisterType.toString())); 1555 } 1556 1557 1558 RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); 1559 assert arrayRegisterType != null; 1560 if (indexRegisterType.category == RegisterType.Category.Unknown) { 1561 return false; 1562 } 1563 1564 if (arrayRegisterType.category != RegisterType.Category.Null) { 1565 if (arrayRegisterType.category != RegisterType.Category.Reference) { 1566 throw new ValidationException(String.format("Cannot use %s with non-array type %s", 1567 analyzedInstruction.instruction.opcode.name, arrayRegisterType.category.toString())); 1568 } 1569 1570 assert arrayRegisterType.type != null; 1571 if (arrayRegisterType.type.getClassType().charAt(0) != '[') { 1572 throw new ValidationException(String.format("Cannot use %s with non-array type %s", 1573 analyzedInstruction.instruction.opcode.name, arrayRegisterType.type.getClassType())); 1574 } 1575 1576 assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef; 1577 ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type; 1578 1579 if (arrayClassDef.getArrayDimensions() != 1) { 1580 throw new ValidationException(String.format("Cannot use %s with multi-dimensional array type %s", 1581 analyzedInstruction.instruction.opcode.name, arrayRegisterType.type.getClassType())); 1582 } 1583 1584 RegisterType arrayBaseType = 1585 RegisterType.getRegisterTypeForType(arrayClassDef.getBaseElementClass().getClassType()); 1586 if (checkArrayFieldAssignment(arrayBaseType.category, instructionCategory)) { 1587 throw new ValidationException(String.format("Cannot use %s with array type %s. Incorrect array type " + 1588 "for the instruction.", analyzedInstruction.instruction.opcode.name, 1589 arrayRegisterType.type.getClassType())); 1590 } 1591 } 1592 1593 return true; 1594 } 1595 1596 private boolean handleAputWide(AnalyzedInstruction analyzedInstruction) { 1597 ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; 1598 1599 RegisterType indexRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterC()); 1600 assert indexRegisterType != null; 1601 if (indexRegisterType.category == RegisterType.Category.Unknown) { 1602 return false; 1603 } 1604 checkRegister(indexRegisterType, Primitive32BitCategories); 1605 1606 RegisterType sourceRegisterType = getAndCheckWideSourcePair(analyzedInstruction, instruction.getRegisterA()); 1607 if (sourceRegisterType.category == RegisterType.Category.Unknown) { 1608 return false; 1609 } 1610 1611 RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); 1612 assert arrayRegisterType != null; 1613 if (indexRegisterType.category == RegisterType.Category.Unknown) { 1614 return false; 1615 } 1616 1617 if (arrayRegisterType.category != RegisterType.Category.Null) { 1618 if (arrayRegisterType.category != RegisterType.Category.Reference) { 1619 throw new ValidationException(String.format("Cannot use aput-wide with non-array type %s", 1620 arrayRegisterType.category.toString())); 1621 } 1622 1623 assert arrayRegisterType.type != null; 1624 if (arrayRegisterType.type.getClassType().charAt(0) != '[') { 1625 throw new ValidationException(String.format("Cannot use aput-wide with non-array type %s", 1626 arrayRegisterType.type.getClassType())); 1627 } 1628 1629 assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef; 1630 ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type; 1631 1632 if (arrayClassDef.getArrayDimensions() != 1) { 1633 throw new ValidationException(String.format("Cannot use aput-wide with multi-dimensional array type %s", 1634 arrayRegisterType.type.getClassType())); 1635 } 1636 1637 char arrayBaseType = arrayClassDef.getBaseElementClass().getClassType().charAt(0); 1638 if (arrayBaseType != 'J' && arrayBaseType != 'D') { 1639 throw new ValidationException(String.format("Cannot use aput-wide with array type %s. Incorrect " + 1640 "array type for the instruction.", arrayRegisterType.type.getClassType())); 1641 } 1642 } 1643 1644 return true; 1645 } 1646 1647 private static boolean checkArrayFieldAssignment(RegisterType.Category arrayFieldCategory, 1648 RegisterType.Category instructionCategory) { 1649 if (arrayFieldCategory == instructionCategory) { 1650 return true; 1651 } 1652 1653 if ((arrayFieldCategory == RegisterType.Category.Integer && 1654 instructionCategory == RegisterType.Category.Float) || 1655 (arrayFieldCategory == RegisterType.Category.Float && 1656 instructionCategory == RegisterType.Category.Integer)) { 1657 return true; 1658 } 1659 return false; 1660 } 1661 1662 private static void checkRegister(RegisterType registerType, EnumSet validCategories) { 1663 if (!validCategories.contains(registerType.category)) { 1664 //TODO: add expected categories to error message 1665 throw new ValidationException("Invalid register type. Expecting one of: " + " but got \"" + 1666 registerType.category + "\""); 1667 } 1668 } 1669 1670 private static void checkWideDestinationPair(AnalyzedInstruction analyzedInstruction) { 1671 int register = analyzedInstruction.getDestinationRegister(); 1672 1673 if (register == (analyzedInstruction.postRegisterMap.length - 1)) { 1674 throw new ValidationException("v" + register + " is the last register and not a valid wide register " + 1675 "pair."); 1676 } 1677 } 1678 1679 private static RegisterType getAndCheckWideSourcePair(AnalyzedInstruction analyzedInstruction, int firstRegister) { 1680 assert firstRegister >= 0 && firstRegister < analyzedInstruction.postRegisterMap.length; 1681 1682 if (firstRegister == analyzedInstruction.postRegisterMap.length - 1) { 1683 throw new ValidationException("v" + firstRegister + " is the last register and not a valid wide register " + 1684 "pair."); 1685 } 1686 1687 RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(firstRegister); 1688 assert registerType != null; 1689 if (registerType.category == RegisterType.Category.Unknown) { 1690 return registerType; 1691 } 1692 checkRegister(registerType, WideLowCategories); 1693 1694 RegisterType secondRegisterType = analyzedInstruction.getPreInstructionRegisterType(firstRegister + 1); 1695 assert secondRegisterType != null; 1696 checkRegister(secondRegisterType, WideHighCategories); 1697 1698 if (( registerType.category == RegisterType.Category.LongLo && 1699 secondRegisterType.category == RegisterType.Category.DoubleHi) 1700 || ( registerType.category == RegisterType.Category.DoubleLo && 1701 secondRegisterType.category == RegisterType.Category.LongHi)) { 1702 assert false; 1703 throw new ValidationException("The first register in the wide register pair isn't the same type (long " + 1704 "vs. double) as the second register in the pair"); 1705 } 1706 1707 return registerType; 1708 } 1709} 1710