1 /* 2 * ProGuard -- shrinking, optimization, obfuscation, and preverification 3 * of Java bytecode. 4 * 5 * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License as published by the Free 9 * Software Foundation; either version 2 of the License, or (at your option) 10 * any later version. 11 * 12 * This program is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 15 * more details. 16 * 17 * You should have received a copy of the GNU General Public License along 18 * with this program; if not, write to the Free Software Foundation, Inc., 19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 */ 21 package proguard.preverify; 22 23 import proguard.classfile.*; 24 import proguard.classfile.attribute.*; 25 import proguard.classfile.attribute.preverification.*; 26 import proguard.classfile.attribute.visitor.AttributeVisitor; 27 import proguard.classfile.editor.*; 28 import proguard.classfile.instruction.InstructionConstants; 29 import proguard.classfile.util.SimplifiedVisitor; 30 import proguard.classfile.visitor.ClassPrinter; 31 import proguard.evaluation.*; 32 import proguard.evaluation.value.*; 33 import proguard.optimize.evaluation.*; 34 35 import java.util.*; 36 37 /** 38 * This class can preverify methods in program class pools, according to a given 39 * specification. 40 * 41 * @author Eric Lafortune 42 */ 43 public class CodePreverifier 44 extends SimplifiedVisitor 45 implements AttributeVisitor 46 { 47 //* 48 private static final boolean DEBUG = false; 49 /*/ 50 private static boolean DEBUG = true; 51 //*/ 52 53 54 private final boolean microEdition; 55 56 private final PartialEvaluator partialEvaluator = new PartialEvaluator(); 57 private final LivenessAnalyzer livenessAnalyzer = new LivenessAnalyzer(partialEvaluator); 58 private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(); 59 60 61 /** 62 * Creates a new CodePreverifier. 63 */ CodePreverifier(boolean microEdition)64 public CodePreverifier(boolean microEdition) 65 { 66 this.microEdition = microEdition; 67 } 68 69 70 // Implementations for AttributeVisitor. 71 visitAnyAttribute(Clazz clazz, Attribute attribute)72 public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} 73 74 visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)75 public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) 76 { 77 // TODO: Remove this when the preverifier has stabilized. 78 // Catch any unexpected exceptions from the actual visiting method. 79 try 80 { 81 // Process the code. 82 visitCodeAttribute0(clazz, method, codeAttribute); 83 } 84 catch (RuntimeException ex) 85 { 86 System.err.println("Unexpected error while preverifying:"); 87 System.err.println(" Class = ["+clazz.getName()+"]"); 88 System.err.println(" Method = ["+method.getName(clazz)+method.getDescriptor(clazz)+"]"); 89 System.err.println(" Exception = ["+ex.getClass().getName()+"] ("+ex.getMessage()+")"); 90 91 throw ex; 92 } 93 } 94 95 visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAttribute)96 public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAttribute) 97 { 98 // DEBUG = 99 // clazz.getName().equals("abc/Def") && 100 // method.getName(clazz).equals("abc"); 101 102 ProgramClass programClass = (ProgramClass)clazz; 103 ProgramMethod programMethod = (ProgramMethod)method; 104 105 int codeLength = codeAttribute.u4codeLength; 106 107 // Evaluate the method. 108 //partialEvaluator.visitCodeAttribute(clazz, method, codeAttribute); 109 livenessAnalyzer.visitCodeAttribute(clazz, method, codeAttribute); 110 111 // We may have to remove unreachable code. 112 codeAttributeEditor.reset(codeLength); 113 114 // Collect the stack map frames. 115 List stackMapFrameList = new ArrayList(); 116 117 for (int offset = 0; offset < codeLength; offset++) 118 { 119 // Only store frames at the beginning of code blocks. 120 if (!partialEvaluator.isTraced(offset)) 121 { 122 // Mark the unreachable instruction for deletion. 123 codeAttributeEditor.deleteInstruction(offset); 124 } 125 else if (partialEvaluator.isBranchOrExceptionTarget(offset)) 126 { 127 // Convert the variable values to types. 128 VerificationType[] variableTypes = 129 correspondingVerificationTypes(programClass, 130 programMethod, 131 codeAttribute, 132 offset, 133 partialEvaluator.getVariablesBefore(offset)); 134 135 // Convert the stack values to types. 136 VerificationType[] stackTypes = 137 correspondingVerificationTypes(programClass, 138 programMethod, 139 codeAttribute, 140 offset, 141 partialEvaluator.getStackBefore(offset)); 142 // Create and store a new frame. 143 stackMapFrameList.add(new FullFrame(offset, 144 variableTypes, 145 stackTypes)); 146 } 147 } 148 149 // Compress the stack map frames if the target is not Java Micro Edition. 150 if (!microEdition && !stackMapFrameList.isEmpty()) 151 { 152 // Convert the initial variable values to types. 153 VerificationType[] initialVariables = 154 correspondingVerificationTypes(programClass, 155 programMethod, 156 codeAttribute, 157 PartialEvaluator.AT_METHOD_ENTRY, 158 partialEvaluator.getVariablesBefore(0)); 159 160 // Special case: the <init> method. 161 if (method.getName(programClass).equals(ClassConstants.METHOD_NAME_INIT)) 162 { 163 initialVariables[0] = VerificationTypeFactory.createUninitializedThisType(); 164 } 165 166 compressStackMapFrames(initialVariables, 167 stackMapFrameList); 168 } 169 170 // Get the proper name for the attribute to be added/replaced/deleted. 171 String stackMapAttributeName = microEdition ? 172 ClassConstants.ATTR_StackMap : 173 ClassConstants.ATTR_StackMapTable; 174 175 int frameCount = stackMapFrameList.size(); 176 177 if (DEBUG) 178 { 179 Attribute originalStackMapAttribute = codeAttribute.getAttribute(clazz, 180 stackMapAttributeName); 181 182 if (originalStackMapAttribute != null) 183 { 184 int originalFrameCount = microEdition ? 185 ((StackMapAttribute)originalStackMapAttribute).u2stackMapFramesCount : 186 ((StackMapTableAttribute)originalStackMapAttribute).u2stackMapFramesCount; 187 188 StackMapFrame[] originalFrames = microEdition ? 189 ((StackMapAttribute)originalStackMapAttribute).stackMapFrames : 190 ((StackMapTableAttribute)originalStackMapAttribute).stackMapFrames; 191 192 if (frameCount != originalFrameCount || 193 !Arrays.equals(stackMapFrameList.toArray(), originalFrames)) 194 { 195 System.out.println("Original preverification ["+clazz.getName()+"]:"); 196 new ClassPrinter().visitProgramMethod(programClass, programMethod); 197 } 198 } 199 else if (frameCount != 0) 200 { 201 System.out.println("Original preverification empty ["+clazz.getName()+"."+method.getName(clazz)+"]"); 202 } 203 } 204 205 if (frameCount == 0) 206 { 207 // Remove any stack map (table) attribute from the code attribute. 208 new AttributesEditor(programClass, programMethod, codeAttribute, true).deleteAttribute(stackMapAttributeName); 209 } 210 else 211 { 212 Attribute stackMapAttribute; 213 214 // Create the appropriate attribute. 215 if (microEdition) 216 { 217 // Copy the frames into an array. 218 FullFrame[] stackMapFrames = new FullFrame[frameCount]; 219 stackMapFrameList.toArray(stackMapFrames); 220 221 // Put the frames into a stack map attribute. 222 stackMapAttribute = new StackMapAttribute(stackMapFrames); 223 } 224 else 225 { 226 // Copy the frames into an array. 227 StackMapFrame[] stackMapFrames = new StackMapFrame[frameCount]; 228 stackMapFrameList.toArray(stackMapFrames); 229 230 // Put the frames into a stack map table attribute. 231 stackMapAttribute = new StackMapTableAttribute(stackMapFrames); 232 } 233 234 // Fill out the name of the stack map attribute. 235 stackMapAttribute.u2attributeNameIndex = 236 new ConstantPoolEditor(programClass).addUtf8Constant(stackMapAttributeName); 237 238 // Add the new stack map (table) attribute to the code attribute. 239 new AttributesEditor(programClass, programMethod, codeAttribute, true).addAttribute(stackMapAttribute); 240 241 if (DEBUG) 242 { 243 System.out.println("Preverifier ["+programClass.getName()+"."+programMethod.getName(programClass)+"]:"); 244 stackMapAttribute.accept(programClass, programMethod, codeAttribute, new ClassPrinter()); 245 } 246 } 247 248 // Apply code modifications, deleting unreachable code. 249 codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute); 250 } 251 252 253 // Small utility methods. 254 255 /** 256 * Creates and returns the verification types corresponding to the given 257 * variables. If necessary, class constants are added to the constant pool 258 * of the given class. 259 */ correspondingVerificationTypes(ProgramClass programClass, ProgramMethod programMethod, CodeAttribute codeAttribute, int offset, TracedVariables variables)260 private VerificationType[] correspondingVerificationTypes(ProgramClass programClass, 261 ProgramMethod programMethod, 262 CodeAttribute codeAttribute, 263 int offset, 264 TracedVariables variables) 265 { 266 int maximumVariablesSize = variables.size(); 267 int typeCount = 0; 268 int typeIndex = 0; 269 270 // Count the the number of verification types, ignoring any nulls at 271 // the end. 272 for (int index = 0; index < maximumVariablesSize; index++) 273 { 274 Value value = variables.getValue(index); 275 276 typeIndex++; 277 278 // Remember the maximum live type index. 279 if (value != null && 280 (offset == PartialEvaluator.AT_METHOD_ENTRY || 281 livenessAnalyzer.isAliveBefore(offset, index))) 282 { 283 typeCount = typeIndex; 284 285 // Category 2 types that are alive are stored as single entries. 286 if (value.isCategory2()) 287 { 288 index++; 289 } 290 } 291 } 292 293 // Create and fill out the verification types. 294 VerificationType[] types = new VerificationType[typeCount]; 295 296 typeIndex = 0; 297 298 // Note the slightly different terminating condition, because the 299 // types may have been truncated. 300 for (int index = 0; typeIndex < typeCount; index++) 301 { 302 Value value = variables.getValue(index); 303 Value producerValue = variables.getProducerValue(index); 304 305 // Fill out the type. 306 VerificationType type; 307 308 if (value != null && 309 (offset == PartialEvaluator.AT_METHOD_ENTRY || 310 livenessAnalyzer.isAliveBefore(offset, index))) 311 { 312 type = correspondingVerificationType(programClass, 313 programMethod, 314 codeAttribute, 315 offset, 316 index == 0, 317 value, 318 producerValue); 319 320 // Category 2 types that are alive are stored as single entries. 321 if (value.isCategory2()) 322 { 323 index++; 324 } 325 } 326 else 327 { 328 type = VerificationTypeFactory.createTopType(); 329 } 330 331 types[typeIndex++] = type; 332 } 333 334 return types; 335 } 336 337 338 /** 339 * Creates and returns the verification types corresponding to the given 340 * stack. If necessary, class constants are added to the constant pool 341 * of the given class. 342 */ correspondingVerificationTypes(ProgramClass programClass, ProgramMethod programMethod, CodeAttribute codeAttribute, int offset, TracedStack stack)343 private VerificationType[] correspondingVerificationTypes(ProgramClass programClass, 344 ProgramMethod programMethod, 345 CodeAttribute codeAttribute, 346 int offset, 347 TracedStack stack) 348 { 349 int maximumStackSize = stack.size(); 350 int typeCount = 0; 351 352 // Count the the number of verification types. 353 for (int index = 0; index < maximumStackSize; index++) 354 { 355 // We have to work down from the top of the stack. 356 Value value = stack.getTop(index); 357 358 typeCount++; 359 360 // Category 2 types are stored as single entries. 361 if (value.isCategory2()) 362 { 363 index++; 364 } 365 } 366 367 // Create and fill out the verification types. 368 VerificationType[] types = new VerificationType[typeCount]; 369 370 int typeIndex = typeCount; 371 372 for (int index = 0; index < maximumStackSize; index++) 373 { 374 // We have to work down from the top of the stack. 375 Value value = stack.getTop(index); 376 Value producerValue = stack.getTopProducerValue(index); 377 378 // Fill out the type. 379 types[--typeIndex] = 380 correspondingVerificationType(programClass, 381 programMethod, 382 codeAttribute, 383 offset, 384 false, 385 value, 386 producerValue); 387 388 // Category 2 types are stored as single entries. 389 if (value.isCategory2()) 390 { 391 index++; 392 } 393 } 394 395 return types; 396 } 397 398 399 /** 400 * Creates and returns the verification type corresponding to the given 401 * value. If necessary, a class constant is added to the constant pool of 402 * the given class. 403 */ correspondingVerificationType(ProgramClass programClass, ProgramMethod programMethod, CodeAttribute codeAttribute, int offset, boolean isVariable0, Value value, Value producerValue)404 private VerificationType correspondingVerificationType(ProgramClass programClass, 405 ProgramMethod programMethod, 406 CodeAttribute codeAttribute, 407 int offset, 408 boolean isVariable0, 409 Value value, 410 Value producerValue) 411 { 412 if (value == null) 413 { 414 return VerificationTypeFactory.createTopType(); 415 } 416 417 int type = value.computationalType(); 418 419 switch (type) 420 { 421 case Value.TYPE_INSTRUCTION_OFFSET: 422 case Value.TYPE_INTEGER: return VerificationTypeFactory.createIntegerType(); 423 case Value.TYPE_LONG: return VerificationTypeFactory.createLongType(); 424 case Value.TYPE_FLOAT: return VerificationTypeFactory.createFloatType(); 425 case Value.TYPE_DOUBLE: return VerificationTypeFactory.createDoubleType(); 426 case Value.TYPE_TOP: return VerificationTypeFactory.createTopType(); 427 case Value.TYPE_REFERENCE: 428 // Is it a Null type? 429 ReferenceValue referenceValue = value.referenceValue(); 430 if (referenceValue.isNull() == Value.ALWAYS) 431 { 432 return VerificationTypeFactory.createNullType(); 433 } 434 435 // Does the reference type have a single producer? 436 if (offset != PartialEvaluator.AT_METHOD_ENTRY) 437 { 438 InstructionOffsetValue producers = producerValue.instructionOffsetValue(); 439 if (producers.instructionOffsetCount() == 1) 440 { 441 int producerOffset = producers.instructionOffset(0); 442 443 // Follow any dup or swap instructions. 444 while (producerOffset != PartialEvaluator.AT_METHOD_ENTRY && 445 isDupOrSwap(codeAttribute.code[producerOffset])) 446 { 447 producers = partialEvaluator.getStackBefore(producerOffset).getTopProducerValue(0).instructionOffsetValue(); 448 producerOffset = producers.minimumValue(); 449 } 450 451 // Are we in an instance initialization method, 452 // before the super initialization, loading "this"? 453 if (partialEvaluator.isInitializer() && 454 offset <= partialEvaluator.superInitializationOffset() && 455 (isVariable0 || 456 producerOffset > PartialEvaluator.AT_METHOD_ENTRY && 457 codeAttribute.code[producerOffset] == InstructionConstants.OP_ALOAD_0)) 458 { 459 // It's an UninitializedThis type. 460 return VerificationTypeFactory.createUninitializedThisType(); 461 } 462 463 // Is the reference type newly created and still 464 // uninitialized? 465 if (producerOffset > PartialEvaluator.AT_METHOD_ENTRY && 466 offset <= partialEvaluator.initializationOffset(producerOffset)) 467 { 468 // It's an Uninitialized type. 469 return VerificationTypeFactory.createUninitializedType(producerOffset); 470 } 471 } 472 } 473 474 // It's an ordinary Object type. 475 return VerificationTypeFactory.createObjectType(createClassConstant(programClass, referenceValue)); 476 } 477 478 throw new IllegalArgumentException("Unknown computational type ["+type+"]"); 479 } 480 481 482 /** 483 * Finds or creates a class constant for the given reference value, and 484 * returns its index in the constant pool. 485 */ createClassConstant(ProgramClass programClass, ReferenceValue referenceValue)486 private int createClassConstant(ProgramClass programClass, 487 ReferenceValue referenceValue) 488 { 489 return new ConstantPoolEditor(programClass).addClassConstant(referenceValue.getType(), 490 referenceValue.getReferencedClass()); 491 } 492 493 494 /** 495 * Compresses the given list of full frames, for use in a stack map table. 496 */ compressStackMapFrames(VerificationType[] initialVariableTypes, List stackMapFrameList)497 private void compressStackMapFrames(VerificationType[] initialVariableTypes, 498 List stackMapFrameList) 499 { 500 int previousVariablesCount = initialVariableTypes.length; 501 VerificationType[] previousVariableTypes = initialVariableTypes; 502 503 int previousOffset = -1; 504 505 for (int index = 0; index < stackMapFrameList.size(); index++) 506 { 507 FullFrame fullFrame = (FullFrame)stackMapFrameList.get(index); 508 509 int variablesCount = fullFrame.variablesCount; 510 VerificationType[] variables = fullFrame.variables; 511 int stackCount = fullFrame.stackCount; 512 VerificationType[] stack = fullFrame.stack; 513 514 // Start computing the compressed frame. 515 // The default is the full frame. 516 StackMapFrame compressedFrame = fullFrame; 517 518 // Are all variables equal? 519 if (variablesCount == previousVariablesCount && 520 equalVerificationTypes(variables, previousVariableTypes, variablesCount)) 521 { 522 // Are the stacks equal? 523 //if (stackCount == previousStackCount && 524 // equalVerificationTypes(stack, previousStack, stackCount)) 525 //{ 526 // // Remove the identical frame. 527 // stackMapFrameList.remove(index--); 528 // 529 // // Move on to the next frame (at the same index). 530 // continue; 531 //} 532 // Is the new stack empty? 533 //else 534 if (stackCount == 0) 535 { 536 compressedFrame = new SameZeroFrame(); 537 } 538 // Does the new stack contain a single element? 539 else if (stackCount == 1) 540 { 541 compressedFrame = new SameOneFrame(stack[0]); 542 } 543 } 544 // Is the stack empty? 545 else if (stackCount == 0) 546 { 547 int additionalVariablesCount = variablesCount - previousVariablesCount; 548 549 // Are the variables chopped? 550 if (additionalVariablesCount < 0 && 551 additionalVariablesCount > -4 && 552 equalVerificationTypes(variables, previousVariableTypes, variablesCount)) 553 { 554 compressedFrame = new LessZeroFrame((byte)-additionalVariablesCount); 555 } 556 // Are the variables extended? 557 else if (//previousVariablesCount > 0 && 558 additionalVariablesCount > 0 && 559 additionalVariablesCount < 4 && 560 equalVerificationTypes(variables, previousVariableTypes, previousVariablesCount)) 561 { 562 // Copy the additional variables into an array. 563 VerificationType[] additionalVariables = new VerificationType[additionalVariablesCount]; 564 System.arraycopy(variables, variablesCount - additionalVariablesCount, 565 additionalVariables, 0, 566 additionalVariablesCount); 567 568 compressedFrame = new MoreZeroFrame(additionalVariables); 569 } 570 } 571 572 // Compress the instruction offset. 573 int offset = fullFrame.u2offsetDelta; 574 compressedFrame.u2offsetDelta = offset - previousOffset - 1; 575 previousOffset = offset; 576 577 // Remember this frame. 578 previousVariablesCount = fullFrame.variablesCount; 579 previousVariableTypes = fullFrame.variables; 580 581 // Replace the full frame. 582 stackMapFrameList.set(index, compressedFrame); 583 } 584 } 585 586 587 /** 588 * Returns whether the given arrays of verification types are equal, up to 589 * the given length. 590 */ equalVerificationTypes(VerificationType[] types1, VerificationType[] types2, int length)591 private boolean equalVerificationTypes(VerificationType[] types1, 592 VerificationType[] types2, 593 int length) 594 { 595 if (length > 0 && 596 (types1.length < length || 597 types2.length < length)) 598 { 599 return false; 600 } 601 602 for (int index = 0; index < length; index++) 603 { 604 if (!types1[index].equals(types2[index])) 605 { 606 return false; 607 } 608 } 609 610 return true; 611 } 612 613 614 /** 615 * Returns whether the given instruction opcode represents a dup or swap 616 * instruction (dup, dup_x1, dup_x2, dup2, dup2_x1, dup2_x2, swap). 617 */ isDupOrSwap(int opcode)618 private boolean isDupOrSwap(int opcode) 619 { 620 return opcode >= InstructionConstants.OP_DUP && 621 opcode <= InstructionConstants.OP_SWAP; 622 } 623 } 624