• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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