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