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     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(Opcodes opcodes, @Nonnull byte[] buf, int offset, boolean verifyMagic) {
     65         super(buf);
     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     public Opcodes getOpcodes() {
    121         return opcodes;
    122     }
    123 
    124     public boolean isOdexFile() {
    125         return false;
    126     }
    127 
    128     @Nonnull
    129     @Override
    130     public Set<? extends DexBackedClassDef> getClasses() {
    131         return new FixedSizeSet<DexBackedClassDef>() {
    132             @Nonnull
    133             @Override
    134             public DexBackedClassDef readItem(int index) {
    135                 return new DexBackedClassDef(DexBackedDexFile.this, getClassDefItemOffset(index));
    136             }
    137 
    138             @Override
    139             public int size() {
    140                 return classCount;
    141             }
    142         };
    143     }
    144 
    145     private static void verifyMagicAndByteOrder(@Nonnull byte[] buf, int offset) {
    146         if (!HeaderItem.verifyMagic(buf, offset)) {
    147             StringBuilder sb = new StringBuilder("Invalid magic value:");
    148             for (int i=0; i<8; i++) {
    149                 sb.append(String.format(" %02x", buf[i]));
    150             }
    151             throw new NotADexFile(sb.toString());
    152         }
    153 
    154         int endian = HeaderItem.getEndian(buf, offset);
    155         if (endian == HeaderItem.BIG_ENDIAN_TAG) {
    156             throw new ExceptionWithContext("Big endian dex files are not currently supported");
    157         }
    158 
    159         if (endian != HeaderItem.LITTLE_ENDIAN_TAG) {
    160             throw new ExceptionWithContext("Invalid endian tag: 0x%x", endian);
    161         }
    162     }
    163 
    164     public int getStringIdItemOffset(int stringIndex) {
    165         if (stringIndex < 0 || stringIndex >= stringCount) {
    166             throw new InvalidItemIndex(stringIndex, "String index out of bounds: %d", stringIndex);
    167         }
    168         return stringStartOffset + stringIndex*StringIdItem.ITEM_SIZE;
    169     }
    170 
    171     public int getTypeIdItemOffset(int typeIndex) {
    172         if (typeIndex < 0 || typeIndex >= typeCount) {
    173             throw new InvalidItemIndex(typeIndex, "Type index out of bounds: %d", typeIndex);
    174         }
    175         return typeStartOffset + typeIndex*TypeIdItem.ITEM_SIZE;
    176     }
    177 
    178     public int getFieldIdItemOffset(int fieldIndex) {
    179         if (fieldIndex < 0 || fieldIndex >= fieldCount) {
    180             throw new InvalidItemIndex(fieldIndex, "Field index out of bounds: %d", fieldIndex);
    181         }
    182         return fieldStartOffset + fieldIndex*FieldIdItem.ITEM_SIZE;
    183     }
    184 
    185     public int getMethodIdItemOffset(int methodIndex) {
    186         if (methodIndex < 0 || methodIndex >= methodCount) {
    187             throw new InvalidItemIndex(methodIndex, "Method index out of bounds: %d", methodIndex);
    188         }
    189         return methodStartOffset + methodIndex*MethodIdItem.ITEM_SIZE;
    190     }
    191 
    192     public int getProtoIdItemOffset(int protoIndex) {
    193         if (protoIndex < 0 || protoIndex >= protoCount) {
    194             throw new InvalidItemIndex(protoIndex, "Proto index out of bounds: %d", protoIndex);
    195         }
    196         return protoStartOffset + protoIndex*ProtoIdItem.ITEM_SIZE;
    197     }
    198 
    199     public int getClassDefItemOffset(int classIndex) {
    200         if (classIndex < 0 || classIndex >= classCount) {
    201             throw new InvalidItemIndex(classIndex, "Class index out of bounds: %d", classIndex);
    202         }
    203         return classStartOffset + classIndex*ClassDefItem.ITEM_SIZE;
    204     }
    205 
    206     public int getClassCount() {
    207         return classCount;
    208     }
    209 
    210     @Nonnull
    211     public String getString(int stringIndex) {
    212         int stringOffset = getStringIdItemOffset(stringIndex);
    213         int stringDataOffset = readSmallUint(stringOffset);
    214         DexReader reader = readerAt(stringDataOffset);
    215         int utf16Length = reader.readSmallUleb128();
    216         return reader.readString(utf16Length);
    217     }
    218 
    219     @Nullable
    220     public String getOptionalString(int stringIndex) {
    221         if (stringIndex == -1) {
    222             return null;
    223         }
    224         return getString(stringIndex);
    225     }
    226 
    227     @Nonnull
    228     public String getType(int typeIndex) {
    229         int typeOffset = getTypeIdItemOffset(typeIndex);
    230         int stringIndex = readSmallUint(typeOffset);
    231         return getString(stringIndex);
    232     }
    233 
    234     @Nullable
    235     public String getOptionalType(int typeIndex) {
    236         if (typeIndex == -1) {
    237             return null;
    238         }
    239         return getType(typeIndex);
    240     }
    241 
    242     @Override
    243     @Nonnull
    244     public DexReader readerAt(int offset) {
    245         return new DexReader(this, offset);
    246     }
    247 
    248     public static class NotADexFile extends RuntimeException {
    249         public NotADexFile() {
    250         }
    251 
    252         public NotADexFile(Throwable cause) {
    253             super(cause);
    254         }
    255 
    256         public NotADexFile(String message) {
    257             super(message);
    258         }
    259 
    260         public NotADexFile(String message, Throwable cause) {
    261             super(message, cause);
    262         }
    263     }
    264 
    265     public static class InvalidItemIndex extends ExceptionWithContext {
    266         private final int itemIndex;
    267 
    268         public InvalidItemIndex(int itemIndex) {
    269             super("");
    270             this.itemIndex = itemIndex;
    271         }
    272 
    273         public InvalidItemIndex(int itemIndex, String message, Object... formatArgs) {
    274             super(message, formatArgs);
    275             this.itemIndex = itemIndex;
    276         }
    277 
    278         public int getInvalidIndex() {
    279             return itemIndex;
    280         }
    281     }
    282 }
    283