1 /* 2 * Copyright 2012, 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.dexbacked; 33 34 import com.google.common.io.ByteStreams; 35 import org.jf.dexlib2.Opcodes; 36 import org.jf.dexlib2.ReferenceType; 37 import org.jf.dexlib2.dexbacked.raw.*; 38 import org.jf.dexlib2.dexbacked.reference.DexBackedFieldReference; 39 import org.jf.dexlib2.dexbacked.reference.DexBackedMethodReference; 40 import org.jf.dexlib2.dexbacked.reference.DexBackedStringReference; 41 import org.jf.dexlib2.dexbacked.reference.DexBackedTypeReference; 42 import org.jf.dexlib2.dexbacked.util.FixedSizeSet; 43 import org.jf.dexlib2.iface.DexFile; 44 import org.jf.dexlib2.iface.reference.Reference; 45 import org.jf.dexlib2.util.DexUtil; 46 import org.jf.util.ExceptionWithContext; 47 48 import javax.annotation.Nonnull; 49 import javax.annotation.Nullable; 50 import java.io.IOException; 51 import java.io.InputStream; 52 import java.util.AbstractList; 53 import java.util.List; 54 import java.util.Set; 55 56 public class DexBackedDexFile extends BaseDexBuffer implements DexFile { 57 @Nonnull private final Opcodes opcodes; 58 59 private final int stringCount; 60 private final int stringStartOffset; 61 private final int typeCount; 62 private final int typeStartOffset; 63 private final int protoCount; 64 private final int protoStartOffset; 65 private final int fieldCount; 66 private final int fieldStartOffset; 67 private final int methodCount; 68 private final int methodStartOffset; 69 private final int classCount; 70 private final int classStartOffset; 71 DexBackedDexFile(@onnull Opcodes opcodes, @Nonnull byte[] buf, int offset, boolean verifyMagic)72 protected DexBackedDexFile(@Nonnull Opcodes opcodes, @Nonnull byte[] buf, int offset, boolean verifyMagic) { 73 super(buf, offset); 74 75 this.opcodes = opcodes; 76 77 if (verifyMagic) { 78 DexUtil.verifyDexHeader(buf, offset); 79 } 80 81 stringCount = readSmallUint(HeaderItem.STRING_COUNT_OFFSET); 82 stringStartOffset = readSmallUint(HeaderItem.STRING_START_OFFSET); 83 typeCount = readSmallUint(HeaderItem.TYPE_COUNT_OFFSET); 84 typeStartOffset = readSmallUint(HeaderItem.TYPE_START_OFFSET); 85 protoCount = readSmallUint(HeaderItem.PROTO_COUNT_OFFSET); 86 protoStartOffset = readSmallUint(HeaderItem.PROTO_START_OFFSET); 87 fieldCount = readSmallUint(HeaderItem.FIELD_COUNT_OFFSET); 88 fieldStartOffset = readSmallUint(HeaderItem.FIELD_START_OFFSET); 89 methodCount = readSmallUint(HeaderItem.METHOD_COUNT_OFFSET); 90 methodStartOffset = readSmallUint(HeaderItem.METHOD_START_OFFSET); 91 classCount = readSmallUint(HeaderItem.CLASS_COUNT_OFFSET); 92 classStartOffset = readSmallUint(HeaderItem.CLASS_START_OFFSET); 93 } 94 DexBackedDexFile(@onnull Opcodes opcodes, @Nonnull BaseDexBuffer buf)95 public DexBackedDexFile(@Nonnull Opcodes opcodes, @Nonnull BaseDexBuffer buf) { 96 this(opcodes, buf.buf, buf.baseOffset); 97 } 98 DexBackedDexFile(@onnull Opcodes opcodes, @Nonnull byte[] buf, int offset)99 public DexBackedDexFile(@Nonnull Opcodes opcodes, @Nonnull byte[] buf, int offset) { 100 this(opcodes, buf, offset, false); 101 } 102 DexBackedDexFile(@onnull Opcodes opcodes, @Nonnull byte[] buf)103 public DexBackedDexFile(@Nonnull Opcodes opcodes, @Nonnull byte[] buf) { 104 this(opcodes, buf, 0, true); 105 } 106 107 @Nonnull fromInputStream(@onnull Opcodes opcodes, @Nonnull InputStream is)108 public static DexBackedDexFile fromInputStream(@Nonnull Opcodes opcodes, @Nonnull InputStream is) 109 throws IOException { 110 DexUtil.verifyDexHeader(is); 111 112 byte[] buf = ByteStreams.toByteArray(is); 113 return new DexBackedDexFile(opcodes, buf, 0, false); 114 } 115 getOpcodes()116 @Override @Nonnull public Opcodes getOpcodes() { 117 return opcodes; 118 } 119 120 // Will only be true for a dalvik-style odex file isOdexFile()121 public boolean isOdexFile() { 122 return false; 123 } 124 125 // Will be true for both a dalvik-style odex file, and an art-style odex file embedded in an oat file hasOdexOpcodes()126 public boolean hasOdexOpcodes() { 127 return false; 128 } 129 130 @Nonnull 131 @Override getClasses()132 public Set<? extends DexBackedClassDef> getClasses() { 133 return new FixedSizeSet<DexBackedClassDef>() { 134 @Nonnull 135 @Override 136 public DexBackedClassDef readItem(int index) { 137 return new DexBackedClassDef(DexBackedDexFile.this, getClassDefItemOffset(index)); 138 } 139 140 @Override 141 public int size() { 142 return classCount; 143 } 144 }; 145 } 146 147 public int getStringIdItemOffset(int stringIndex) { 148 if (stringIndex < 0 || stringIndex >= stringCount) { 149 throw new InvalidItemIndex(stringIndex, "String index out of bounds: %d", stringIndex); 150 } 151 return stringStartOffset + stringIndex*StringIdItem.ITEM_SIZE; 152 } 153 154 public int getTypeIdItemOffset(int typeIndex) { 155 if (typeIndex < 0 || typeIndex >= typeCount) { 156 throw new InvalidItemIndex(typeIndex, "Type index out of bounds: %d", typeIndex); 157 } 158 return typeStartOffset + typeIndex*TypeIdItem.ITEM_SIZE; 159 } 160 161 public int getFieldIdItemOffset(int fieldIndex) { 162 if (fieldIndex < 0 || fieldIndex >= fieldCount) { 163 throw new InvalidItemIndex(fieldIndex, "Field index out of bounds: %d", fieldIndex); 164 } 165 return fieldStartOffset + fieldIndex*FieldIdItem.ITEM_SIZE; 166 } 167 168 public int getMethodIdItemOffset(int methodIndex) { 169 if (methodIndex < 0 || methodIndex >= methodCount) { 170 throw new InvalidItemIndex(methodIndex, "Method index out of bounds: %d", methodIndex); 171 } 172 return methodStartOffset + methodIndex*MethodIdItem.ITEM_SIZE; 173 } 174 175 public int getProtoIdItemOffset(int protoIndex) { 176 if (protoIndex < 0 || protoIndex >= protoCount) { 177 throw new InvalidItemIndex(protoIndex, "Proto index out of bounds: %d", protoIndex); 178 } 179 return protoStartOffset + protoIndex*ProtoIdItem.ITEM_SIZE; 180 } 181 182 public int getClassDefItemOffset(int classIndex) { 183 if (classIndex < 0 || classIndex >= classCount) { 184 throw new InvalidItemIndex(classIndex, "Class index out of bounds: %d", classIndex); 185 } 186 return classStartOffset + classIndex*ClassDefItem.ITEM_SIZE; 187 } 188 189 public int getClassCount() { 190 return classCount; 191 } 192 193 public int getStringCount() { 194 return stringCount; 195 } 196 197 public int getTypeCount() { 198 return typeCount; 199 } 200 201 public int getProtoCount() { 202 return protoCount; 203 } 204 205 public int getFieldCount() { 206 return fieldCount; 207 } 208 209 public int getMethodCount() { 210 return methodCount; 211 } 212 213 @Nonnull 214 public String getString(int stringIndex) { 215 int stringOffset = getStringIdItemOffset(stringIndex); 216 int stringDataOffset = readSmallUint(stringOffset); 217 DexReader reader = readerAt(stringDataOffset); 218 int utf16Length = reader.readSmallUleb128(); 219 return reader.readString(utf16Length); 220 } 221 222 @Nullable 223 public String getOptionalString(int stringIndex) { 224 if (stringIndex == -1) { 225 return null; 226 } 227 return getString(stringIndex); 228 } 229 230 @Nonnull 231 public String getType(int typeIndex) { 232 int typeOffset = getTypeIdItemOffset(typeIndex); 233 int stringIndex = readSmallUint(typeOffset); 234 return getString(stringIndex); 235 } 236 237 @Nullable 238 public String getOptionalType(int typeIndex) { 239 if (typeIndex == -1) { 240 return null; 241 } 242 return getType(typeIndex); 243 } 244 245 public List<DexBackedStringReference> getStrings() { 246 return new AbstractList<DexBackedStringReference>() { 247 @Override public DexBackedStringReference get(int index) { 248 if (index < 0 || index >= getStringCount()) { 249 throw new IndexOutOfBoundsException(); 250 } 251 return new DexBackedStringReference(DexBackedDexFile.this, index); 252 } 253 254 @Override public int size() { 255 return getStringCount(); 256 } 257 }; 258 } 259 260 public List<DexBackedTypeReference> getTypes() { 261 return new AbstractList<DexBackedTypeReference>() { 262 @Override public DexBackedTypeReference get(int index) { 263 if (index < 0 || index >= getTypeCount()) { 264 throw new IndexOutOfBoundsException(); 265 } 266 return new DexBackedTypeReference(DexBackedDexFile.this, index); 267 } 268 269 @Override public int size() { 270 return getTypeCount(); 271 } 272 }; 273 } 274 275 public List<DexBackedMethodReference> getMethods() { 276 return new AbstractList<DexBackedMethodReference>() { 277 @Override public DexBackedMethodReference get(int index) { 278 if (index < 0 || index >= getMethodCount()) { 279 throw new IndexOutOfBoundsException(); 280 } 281 return new DexBackedMethodReference(DexBackedDexFile.this, index); 282 } 283 284 @Override public int size() { 285 return getMethodCount(); 286 } 287 }; 288 } 289 290 public List<DexBackedFieldReference> getFields() { 291 return new AbstractList<DexBackedFieldReference>() { 292 @Override public DexBackedFieldReference get(int index) { 293 if (index < 0 || index >= getFieldCount()) { 294 throw new IndexOutOfBoundsException(); 295 } 296 return new DexBackedFieldReference(DexBackedDexFile.this, index); 297 } 298 299 @Override public int size() { 300 return getFieldCount(); 301 } 302 }; 303 } 304 305 public List<? extends Reference> getReferences(int referenceType) { 306 switch (referenceType) { 307 case ReferenceType.STRING: 308 return getStrings(); 309 case ReferenceType.TYPE: 310 return getTypes(); 311 case ReferenceType.METHOD: 312 return getMethods(); 313 case ReferenceType.FIELD: 314 return getFields(); 315 default: 316 throw new IllegalArgumentException(String.format("Invalid reference type: %d", referenceType)); 317 } 318 } 319 320 @Override 321 @Nonnull 322 public DexReader readerAt(int offset) { 323 return new DexReader(this, offset); 324 } 325 326 public static class NotADexFile extends RuntimeException { 327 public NotADexFile() { 328 } 329 330 public NotADexFile(Throwable cause) { 331 super(cause); 332 } 333 334 public NotADexFile(String message) { 335 super(message); 336 } 337 338 public NotADexFile(String message, Throwable cause) { 339 super(message, cause); 340 } 341 } 342 343 public static class InvalidItemIndex extends ExceptionWithContext { 344 private final int itemIndex; 345 346 public InvalidItemIndex(int itemIndex) { 347 super(""); 348 this.itemIndex = itemIndex; 349 } 350 351 public InvalidItemIndex(int itemIndex, String message, Object... formatArgs) { 352 super(message, formatArgs); 353 this.itemIndex = itemIndex; 354 } 355 356 public int getInvalidIndex() { 357 return itemIndex; 358 } 359 } 360 } 361