Home | History | Annotate | Download | only in dexbacked
      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.dexbacked.raw.*;
     37 import org.jf.dexlib2.dexbacked.util.FixedSizeSet;
     38 import org.jf.dexlib2.iface.DexFile;
     39 import org.jf.util.ExceptionWithContext;
     40 
     41 import javax.annotation.Nonnull;
     42 import javax.annotation.Nullable;
     43 import java.io.EOFException;
     44 import java.io.IOException;
     45 import java.io.InputStream;
     46 import java.util.Set;
     47 
     48 public class DexBackedDexFile extends BaseDexBuffer implements DexFile {
     49     @Nonnull private final Opcodes opcodes;
     50 
     51     private final int stringCount;
     52     private final int stringStartOffset;
     53     private final int typeCount;
     54     private final int typeStartOffset;
     55     private final int protoCount;
     56     private final int protoStartOffset;
     57     private final int fieldCount;
     58     private final int fieldStartOffset;
     59     private final int methodCount;
     60     private final int methodStartOffset;
     61     private final int classCount;
     62     private final int classStartOffset;
     63 
     64     private DexBackedDexFile(@Nonnull Opcodes opcodes, @Nonnull byte[] buf, int offset, boolean verifyMagic) {
     65         super(buf, offset);
     66 
     67         this.opcodes = opcodes;
     68 
     69         if (verifyMagic) {
     70             verifyMagicAndByteOrder(buf, offset);
     71         }
     72 
     73         stringCount = readSmallUint(HeaderItem.STRING_COUNT_OFFSET);
     74         stringStartOffset = readSmallUint(HeaderItem.STRING_START_OFFSET);
     75         typeCount = readSmallUint(HeaderItem.TYPE_COUNT_OFFSET);
     76         typeStartOffset = readSmallUint(HeaderItem.TYPE_START_OFFSET);
     77         protoCount = readSmallUint(HeaderItem.PROTO_COUNT_OFFSET);
     78         protoStartOffset = readSmallUint(HeaderItem.PROTO_START_OFFSET);
     79         fieldCount = readSmallUint(HeaderItem.FIELD_COUNT_OFFSET);
     80         fieldStartOffset = readSmallUint(HeaderItem.FIELD_START_OFFSET);
     81         methodCount = readSmallUint(HeaderItem.METHOD_COUNT_OFFSET);
     82         methodStartOffset = readSmallUint(HeaderItem.METHOD_START_OFFSET);
     83         classCount = readSmallUint(HeaderItem.CLASS_COUNT_OFFSET);
     84         classStartOffset = readSmallUint(HeaderItem.CLASS_START_OFFSET);
     85     }
     86 
     87     public DexBackedDexFile(@Nonnull Opcodes opcodes, @Nonnull BaseDexBuffer buf) {
     88         this(opcodes, buf.buf);
     89     }
     90 
     91     public DexBackedDexFile(@Nonnull Opcodes opcodes, @Nonnull byte[] buf, int offset) {
     92         this(opcodes, buf, offset, false);
     93     }
     94 
     95     public DexBackedDexFile(@Nonnull Opcodes opcodes, @Nonnull byte[] buf) {
     96         this(opcodes, buf, 0, true);
     97     }
     98 
     99     public static DexBackedDexFile fromInputStream(@Nonnull Opcodes opcodes, @Nonnull InputStream is)
    100             throws IOException {
    101         if (!is.markSupported()) {
    102             throw new IllegalArgumentException("InputStream must support mark");
    103         }
    104         is.mark(44);
    105         byte[] partialHeader = new byte[44];
    106         try {
    107             ByteStreams.readFully(is, partialHeader);
    108         } catch (EOFException ex) {
    109             throw new NotADexFile("File is too short");
    110         } finally {
    111             is.reset();
    112         }
    113 
    114         verifyMagicAndByteOrder(partialHeader, 0);
    115 
    116         byte[] buf = ByteStreams.toByteArray(is);
    117         return new DexBackedDexFile(opcodes, buf, 0, false);
    118     }
    119 
    120     @Override @Nonnull public Opcodes getOpcodes() {
    121         return opcodes;
    122     }
    123 
    124     // Will only be true for a dalvik-style odex file
    125     public boolean isOdexFile() {
    126         return false;
    127     }
    128 
    129     // Will be true for both a dalvik-style odex file, and an art-style odex file embedded in an oat file
    130     public boolean hasOdexOpcodes() {
    131         return false;
    132     }
    133 
    134     @Nonnull
    135     @Override
    136     public Set<? extends DexBackedClassDef> getClasses() {
    137         return new FixedSizeSet<DexBackedClassDef>() {
    138             @Nonnull
    139             @Override
    140             public DexBackedClassDef readItem(int index) {
    141                 return new DexBackedClassDef(DexBackedDexFile.this, getClassDefItemOffset(index));
    142             }
    143 
    144             @Override
    145             public int size() {
    146                 return classCount;
    147             }
    148         };
    149     }
    150 
    151     private static void verifyMagicAndByteOrder(@Nonnull byte[] buf, int offset) {
    152         if (!HeaderItem.verifyMagic(buf, offset)) {
    153             StringBuilder sb = new StringBuilder("Invalid magic value:");
    154             for (int i=0; i<8; i++) {
    155                 sb.append(String.format(" %02x", buf[i]));
    156             }
    157             throw new NotADexFile(sb.toString());
    158         }
    159 
    160         int endian = HeaderItem.getEndian(buf, offset);
    161         if (endian == HeaderItem.BIG_ENDIAN_TAG) {
    162             throw new ExceptionWithContext("Big endian dex files are not currently supported");
    163         }
    164 
    165         if (endian != HeaderItem.LITTLE_ENDIAN_TAG) {
    166             throw new ExceptionWithContext("Invalid endian tag: 0x%x", endian);
    167         }
    168     }
    169 
    170     public int getStringIdItemOffset(int stringIndex) {
    171         if (stringIndex < 0 || stringIndex >= stringCount) {
    172             throw new InvalidItemIndex(stringIndex, "String index out of bounds: %d", stringIndex);
    173         }
    174         return stringStartOffset + stringIndex*StringIdItem.ITEM_SIZE;
    175     }
    176 
    177     public int getTypeIdItemOffset(int typeIndex) {
    178         if (typeIndex < 0 || typeIndex >= typeCount) {
    179             throw new InvalidItemIndex(typeIndex, "Type index out of bounds: %d", typeIndex);
    180         }
    181         return typeStartOffset + typeIndex*TypeIdItem.ITEM_SIZE;
    182     }
    183 
    184     public int getFieldIdItemOffset(int fieldIndex) {
    185         if (fieldIndex < 0 || fieldIndex >= fieldCount) {
    186             throw new InvalidItemIndex(fieldIndex, "Field index out of bounds: %d", fieldIndex);
    187         }
    188         return fieldStartOffset + fieldIndex*FieldIdItem.ITEM_SIZE;
    189     }
    190 
    191     public int getMethodIdItemOffset(int methodIndex) {
    192         if (methodIndex < 0 || methodIndex >= methodCount) {
    193             throw new InvalidItemIndex(methodIndex, "Method index out of bounds: %d", methodIndex);
    194         }
    195         return methodStartOffset + methodIndex*MethodIdItem.ITEM_SIZE;
    196     }
    197 
    198     public int getProtoIdItemOffset(int protoIndex) {
    199         if (protoIndex < 0 || protoIndex >= protoCount) {
    200             throw new InvalidItemIndex(protoIndex, "Proto index out of bounds: %d", protoIndex);
    201         }
    202         return protoStartOffset + protoIndex*ProtoIdItem.ITEM_SIZE;
    203     }
    204 
    205     public int getClassDefItemOffset(int classIndex) {
    206         if (classIndex < 0 || classIndex >= classCount) {
    207             throw new InvalidItemIndex(classIndex, "Class index out of bounds: %d", classIndex);
    208         }
    209         return classStartOffset + classIndex*ClassDefItem.ITEM_SIZE;
    210     }
    211 
    212     public int getClassCount() {
    213         return classCount;
    214     }
    215 
    216     public int getStringCount() {
    217         return stringCount;
    218     }
    219 
    220     public int getTypeCount() {
    221         return typeCount;
    222     }
    223 
    224     public int getProtoCount() {
    225         return protoCount;
    226     }
    227 
    228     public int getFieldCount() {
    229         return fieldCount;
    230     }
    231 
    232     public int getMethodCount() {
    233         return methodCount;
    234     }
    235 
    236     @Nonnull
    237     public String getString(int stringIndex) {
    238         int stringOffset = getStringIdItemOffset(stringIndex);
    239         int stringDataOffset = readSmallUint(stringOffset);
    240         DexReader reader = readerAt(stringDataOffset);
    241         int utf16Length = reader.readSmallUleb128();
    242         return reader.readString(utf16Length);
    243     }
    244 
    245     @Nullable
    246     public String getOptionalString(int stringIndex) {
    247         if (stringIndex == -1) {
    248             return null;
    249         }
    250         return getString(stringIndex);
    251     }
    252 
    253     @Nonnull
    254     public String getType(int typeIndex) {
    255         int typeOffset = getTypeIdItemOffset(typeIndex);
    256         int stringIndex = readSmallUint(typeOffset);
    257         return getString(stringIndex);
    258     }
    259 
    260     @Nullable
    261     public String getOptionalType(int typeIndex) {
    262         if (typeIndex == -1) {
    263             return null;
    264         }
    265         return getType(typeIndex);
    266     }
    267 
    268     @Override
    269     @Nonnull
    270     public DexReader readerAt(int offset) {
    271         return new DexReader(this, offset);
    272     }
    273 
    274     public static class NotADexFile extends RuntimeException {
    275         public NotADexFile() {
    276         }
    277 
    278         public NotADexFile(Throwable cause) {
    279             super(cause);
    280         }
    281 
    282         public NotADexFile(String message) {
    283             super(message);
    284         }
    285 
    286         public NotADexFile(String message, Throwable cause) {
    287             super(message, cause);
    288         }
    289     }
    290 
    291     public static class InvalidItemIndex extends ExceptionWithContext {
    292         private final int itemIndex;
    293 
    294         public InvalidItemIndex(int itemIndex) {
    295             super("");
    296             this.itemIndex = itemIndex;
    297         }
    298 
    299         public InvalidItemIndex(int itemIndex, String message, Object... formatArgs) {
    300             super(message, formatArgs);
    301             this.itemIndex = itemIndex;
    302         }
    303 
    304         public int getInvalidIndex() {
    305             return itemIndex;
    306         }
    307     }
    308 }
    309