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