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 FieldOpcode(char type, @Nonnull Opcode normalOpcode, @Nullable Opcode quickOpcode, @Nullable Opcode volatileOpcode)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 FieldOpcode(char type, boolean isStatic, @Nonnull Opcode normalOpcode, @Nullable Opcode volatileOpcode)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 FieldOpcode(char type, @Nonnull Opcode normalOpcode, @Nullable Opcode quickOpcode)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 getValueType(char type)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 getTypeIndex(char type)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 isGet(@onnull Opcode opcode)204 private static boolean isGet(@Nonnull Opcode opcode) { 205 return (opcode.flags & Opcode.SETS_REGISTER) != 0; 206 } 207 isStatic(@onnull Opcode opcode)208 private static boolean isStatic(@Nonnull Opcode opcode) { 209 return (opcode.flags & Opcode.STATIC_FIELD_ACCESSOR) != 0; 210 } 211 OdexedFieldInstructionMapper(boolean isArt)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 getAndCheckDeodexedOpcode(@onnull String fieldType, @Nonnull Opcode odexedOpcode)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 isCompatible(Opcode opcode, char type)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