Home | History | Annotate | Download | only in analysis
      1 /*
      2  * Copyright 2013, Google Inc.
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are
      7  * met:
      8  *
      9  *     * Redistributions of source code must retain the above copyright
     10  * notice, this list of conditions and the following disclaimer.
     11  *     * Redistributions in binary form must reproduce the above
     12  * copyright notice, this list of conditions and the following disclaimer
     13  * in the documentation and/or other materials provided with the
     14  * distribution.
     15  *     * Neither the name of Google Inc. nor the names of its
     16  * contributors may be used to endorse or promote products derived from
     17  * this software without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 package org.jf.dexlib2.analysis;
     33 
     34 import org.jf.dexlib2.Opcode;
     35 
     36 import javax.annotation.Nonnull;
     37 import javax.annotation.Nullable;
     38 import java.util.HashMap;
     39 import java.util.Map;
     40 
     41 public class OdexedFieldInstructionMapper {
     42 
     43     private static final int GET = 0;
     44     private static final int PUT = 1;
     45 
     46     private static final int INSTANCE = 0;
     47     private static final int STATIC = 1;
     48 
     49     private static final int PRIMITIVE = 0;
     50     private static final int WIDE = 1;
     51     private static final int REFERENCE = 2;
     52 
     53     private static class FieldOpcode {
     54         public final char type;
     55         public final boolean isStatic;
     56         @Nonnull public final Opcode normalOpcode;
     57         @Nullable public final Opcode quickOpcode;
     58         @Nullable public final Opcode volatileOpcode;
     59 
     60         public FieldOpcode(char type, @Nonnull Opcode normalOpcode, @Nullable Opcode quickOpcode,
     61                            @Nullable Opcode volatileOpcode) {
     62             this.type = type;
     63             this.isStatic = false;
     64             this.normalOpcode = normalOpcode;
     65             this.quickOpcode = quickOpcode;
     66             this.volatileOpcode = volatileOpcode;
     67         }
     68 
     69         public FieldOpcode(char type, boolean isStatic, @Nonnull Opcode normalOpcode, @Nullable Opcode volatileOpcode) {
     70             this.type = type;
     71             this.isStatic = isStatic;
     72             this.normalOpcode = normalOpcode;
     73             this.quickOpcode = null;
     74             this.volatileOpcode = volatileOpcode;
     75         }
     76 
     77         public FieldOpcode(char type, @Nonnull Opcode normalOpcode, @Nullable Opcode quickOpcode) {
     78             this.type = type;
     79             this.isStatic = false;
     80             this.normalOpcode = normalOpcode;
     81             this.quickOpcode = quickOpcode;
     82             this.volatileOpcode = null;
     83         }
     84     }
     85 
     86     private static final FieldOpcode[] dalvikFieldOpcodes = new FieldOpcode[] {
     87             new FieldOpcode('Z', Opcode.IGET_BOOLEAN, Opcode.IGET_QUICK, Opcode.IGET_VOLATILE),
     88             new FieldOpcode('B', Opcode.IGET_BYTE, Opcode.IGET_QUICK, Opcode.IGET_VOLATILE),
     89             new FieldOpcode('S', Opcode.IGET_SHORT, Opcode.IGET_QUICK, Opcode.IGET_VOLATILE),
     90             new FieldOpcode('C', Opcode.IGET_CHAR, Opcode.IGET_QUICK, Opcode.IGET_VOLATILE),
     91             new FieldOpcode('I', Opcode.IGET, Opcode.IGET_QUICK, Opcode.IGET_VOLATILE),
     92             new FieldOpcode('F', Opcode.IGET, Opcode.IGET_QUICK, Opcode.IGET_VOLATILE),
     93             new FieldOpcode('J', Opcode.IGET_WIDE, Opcode.IGET_WIDE_QUICK, Opcode.IGET_WIDE_VOLATILE),
     94             new FieldOpcode('D', Opcode.IGET_WIDE, Opcode.IGET_WIDE_QUICK, Opcode.IGET_WIDE_VOLATILE),
     95             new FieldOpcode('L', Opcode.IGET_OBJECT, Opcode.IGET_OBJECT_QUICK, Opcode.IGET_OBJECT_VOLATILE),
     96             new FieldOpcode('[', Opcode.IGET_OBJECT, Opcode.IGET_OBJECT_QUICK, Opcode.IGET_OBJECT_VOLATILE),
     97 
     98             new FieldOpcode('Z', Opcode.IPUT_BOOLEAN, Opcode.IPUT_QUICK, Opcode.IPUT_VOLATILE),
     99             new FieldOpcode('B', Opcode.IPUT_BYTE, Opcode.IPUT_QUICK, Opcode.IPUT_VOLATILE),
    100             new FieldOpcode('S', Opcode.IPUT_SHORT, Opcode.IPUT_QUICK, Opcode.IPUT_VOLATILE),
    101             new FieldOpcode('C', Opcode.IPUT_CHAR, Opcode.IPUT_QUICK, Opcode.IPUT_VOLATILE),
    102             new FieldOpcode('I', Opcode.IPUT, Opcode.IPUT_QUICK, Opcode.IPUT_VOLATILE),
    103             new FieldOpcode('F', Opcode.IPUT, Opcode.IPUT_QUICK, Opcode.IPUT_VOLATILE),
    104             new FieldOpcode('J', Opcode.IPUT_WIDE, Opcode.IPUT_WIDE_QUICK, Opcode.IPUT_WIDE_VOLATILE),
    105             new FieldOpcode('D', Opcode.IPUT_WIDE, Opcode.IPUT_WIDE_QUICK, Opcode.IPUT_WIDE_VOLATILE),
    106             new FieldOpcode('L', Opcode.IPUT_OBJECT, Opcode.IPUT_OBJECT_QUICK, Opcode.IPUT_OBJECT_VOLATILE),
    107             new FieldOpcode('[', Opcode.IPUT_OBJECT, Opcode.IPUT_OBJECT_QUICK, Opcode.IPUT_OBJECT_VOLATILE),
    108 
    109             new FieldOpcode('Z', true, Opcode.SPUT_BOOLEAN, Opcode.SPUT_VOLATILE),
    110             new FieldOpcode('B', true, Opcode.SPUT_BYTE, Opcode.SPUT_VOLATILE),
    111             new FieldOpcode('S', true, Opcode.SPUT_SHORT, Opcode.SPUT_VOLATILE),
    112             new FieldOpcode('C', true, Opcode.SPUT_CHAR, Opcode.SPUT_VOLATILE),
    113             new FieldOpcode('I', true, Opcode.SPUT, Opcode.SPUT_VOLATILE),
    114             new FieldOpcode('F', true, Opcode.SPUT, Opcode.SPUT_VOLATILE),
    115             new FieldOpcode('J', true, Opcode.SPUT_WIDE, Opcode.SPUT_WIDE_VOLATILE),
    116             new FieldOpcode('D', true, Opcode.SPUT_WIDE, Opcode.SPUT_WIDE_VOLATILE),
    117             new FieldOpcode('L', true, Opcode.SPUT_OBJECT, Opcode.SPUT_OBJECT_VOLATILE),
    118             new FieldOpcode('[', true, Opcode.SPUT_OBJECT, Opcode.SPUT_OBJECT_VOLATILE),
    119 
    120             new FieldOpcode('Z', true, Opcode.SGET_BOOLEAN, Opcode.SGET_VOLATILE),
    121             new FieldOpcode('B', true, Opcode.SGET_BYTE, Opcode.SGET_VOLATILE),
    122             new FieldOpcode('S', true, Opcode.SGET_SHORT, Opcode.SGET_VOLATILE),
    123             new FieldOpcode('C', true, Opcode.SGET_CHAR, Opcode.SGET_VOLATILE),
    124             new FieldOpcode('I', true, Opcode.SGET, Opcode.SGET_VOLATILE),
    125             new FieldOpcode('F', true, Opcode.SGET, Opcode.SGET_VOLATILE),
    126             new FieldOpcode('J', true, Opcode.SGET_WIDE, Opcode.SGET_WIDE_VOLATILE),
    127             new FieldOpcode('D', true, Opcode.SGET_WIDE, Opcode.SGET_WIDE_VOLATILE),
    128             new FieldOpcode('L', true, Opcode.SGET_OBJECT, Opcode.SGET_OBJECT_VOLATILE),
    129             new FieldOpcode('[', true, Opcode.SGET_OBJECT, Opcode.SGET_OBJECT_VOLATILE),
    130     };
    131 
    132     private static final FieldOpcode[] artFieldOpcodes = new FieldOpcode[] {
    133             new FieldOpcode('Z', Opcode.IGET_BOOLEAN, Opcode.IGET_BOOLEAN_QUICK),
    134             new FieldOpcode('B', Opcode.IGET_BYTE, Opcode.IGET_BYTE_QUICK),
    135             new FieldOpcode('S', Opcode.IGET_SHORT, Opcode.IGET_SHORT_QUICK),
    136             new FieldOpcode('C', Opcode.IGET_CHAR, Opcode.IGET_CHAR_QUICK),
    137             new FieldOpcode('I', Opcode.IGET, Opcode.IGET_QUICK),
    138             new FieldOpcode('F', Opcode.IGET, Opcode.IGET_QUICK),
    139             new FieldOpcode('J', Opcode.IGET_WIDE, Opcode.IGET_WIDE_QUICK),
    140             new FieldOpcode('D', Opcode.IGET_WIDE, Opcode.IGET_WIDE_QUICK),
    141             new FieldOpcode('L', Opcode.IGET_OBJECT, Opcode.IGET_OBJECT_QUICK),
    142             new FieldOpcode('[', Opcode.IGET_OBJECT, Opcode.IGET_OBJECT_QUICK),
    143 
    144             new FieldOpcode('Z', Opcode.IPUT_BOOLEAN, Opcode.IPUT_BOOLEAN_QUICK),
    145             new FieldOpcode('B', Opcode.IPUT_BYTE, Opcode.IPUT_BYTE_QUICK),
    146             new FieldOpcode('S', Opcode.IPUT_SHORT, Opcode.IPUT_SHORT_QUICK),
    147             new FieldOpcode('C', Opcode.IPUT_CHAR, Opcode.IPUT_CHAR_QUICK),
    148             new FieldOpcode('I', Opcode.IPUT, Opcode.IPUT_QUICK),
    149             new FieldOpcode('F', Opcode.IPUT, Opcode.IPUT_QUICK),
    150             new FieldOpcode('J', Opcode.IPUT_WIDE, Opcode.IPUT_WIDE_QUICK),
    151             new FieldOpcode('D', Opcode.IPUT_WIDE, Opcode.IPUT_WIDE_QUICK),
    152             new FieldOpcode('L', Opcode.IPUT_OBJECT, Opcode.IPUT_OBJECT_QUICK),
    153             new FieldOpcode('[', Opcode.IPUT_OBJECT, Opcode.IPUT_OBJECT_QUICK)
    154     };
    155 
    156     private final FieldOpcode[][][] opcodeMap = new FieldOpcode[2][2][10];
    157     private final Map<Opcode, Integer> opcodeValueTypeMap = new HashMap<Opcode, Integer>(30);
    158 
    159     private static int getValueType(char type) {
    160         switch (type) {
    161             case 'Z':
    162             case 'B':
    163             case 'S':
    164             case 'C':
    165             case 'I':
    166             case 'F':
    167                 return PRIMITIVE;
    168             case 'J':
    169             case 'D':
    170                 return WIDE;
    171             case 'L':
    172             case '[':
    173                 return REFERENCE;
    174         }
    175         throw new RuntimeException(String.format("Unknown type %s: ", type));
    176     }
    177 
    178     private static int getTypeIndex(char type) {
    179         switch (type) {
    180             case 'Z':
    181                 return 0;
    182             case 'B':
    183                 return 1;
    184             case 'S':
    185                 return 2;
    186             case 'C':
    187                 return 3;
    188             case 'I':
    189                 return 4;
    190             case 'F':
    191                 return 5;
    192             case 'J':
    193                 return 6;
    194             case 'D':
    195                 return 7;
    196             case 'L':
    197                 return 8;
    198             case '[':
    199                 return 9;
    200         }
    201         throw new RuntimeException(String.format("Unknown type %s: ", type));
    202     }
    203 
    204     private static boolean isGet(@Nonnull Opcode opcode) {
    205         return (opcode.flags & Opcode.SETS_REGISTER) != 0;
    206     }
    207 
    208     private static boolean isStatic(@Nonnull Opcode opcode) {
    209         return (opcode.flags & Opcode.STATIC_FIELD_ACCESSOR) != 0;
    210     }
    211 
    212     public OdexedFieldInstructionMapper(boolean isArt) {
    213         FieldOpcode[] opcodes;
    214         if (isArt) {
    215             opcodes = artFieldOpcodes;
    216         } else {
    217             opcodes = dalvikFieldOpcodes;
    218         }
    219 
    220         for (FieldOpcode fieldOpcode: opcodes) {
    221             opcodeMap[isGet(fieldOpcode.normalOpcode)?GET:PUT]
    222                     [isStatic(fieldOpcode.normalOpcode)?STATIC:INSTANCE]
    223                     [getTypeIndex(fieldOpcode.type)] = fieldOpcode;
    224 
    225             if (fieldOpcode.quickOpcode != null) {
    226                 opcodeValueTypeMap.put(fieldOpcode.quickOpcode, getValueType(fieldOpcode.type));
    227             }
    228             if (fieldOpcode.volatileOpcode != null) {
    229                 opcodeValueTypeMap.put(fieldOpcode.volatileOpcode, getValueType(fieldOpcode.type));
    230             }
    231         }
    232     }
    233 
    234     @Nonnull
    235     public Opcode getAndCheckDeodexedOpcode(@Nonnull String fieldType, @Nonnull Opcode odexedOpcode) {
    236         FieldOpcode fieldOpcode = opcodeMap[isGet(odexedOpcode)?GET:PUT]
    237                 [isStatic(odexedOpcode)?STATIC:INSTANCE]
    238                 [getTypeIndex(fieldType.charAt(0))];
    239 
    240         if (!isCompatible(odexedOpcode, fieldOpcode.type)) {
    241             throw new AnalysisException(String.format("Incorrect field type \"%s\" for %s", fieldType,
    242                     odexedOpcode.name));
    243         }
    244 
    245         return fieldOpcode.normalOpcode;
    246     }
    247 
    248     private boolean isCompatible(Opcode opcode, char type) {
    249         Integer valueType = opcodeValueTypeMap.get(opcode);
    250         if (valueType == null) {
    251             throw new RuntimeException("Unexpected opcode: " + opcode.name);
    252         }
    253         return valueType == getValueType(type);
    254     }
    255 }
    256 
    257 
    258