Home | History | Annotate | Download | only in dexdeps
      1 /*
      2  * Copyright (C) 2009 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.dexdeps;
     18 
     19 import java.io.IOException;
     20 import java.io.RandomAccessFile;
     21 import java.util.Arrays;
     22 
     23 /**
     24  * Data extracted from a DEX file.
     25  */
     26 public class DexData {
     27     private RandomAccessFile mDexFile;
     28     private HeaderItem mHeaderItem;
     29     private String[] mStrings;              // strings from string_data_*
     30     private TypeIdItem[] mTypeIds;
     31     private ProtoIdItem[] mProtoIds;
     32     private FieldIdItem[] mFieldIds;
     33     private MethodIdItem[] mMethodIds;
     34     private ClassDefItem[] mClassDefs;
     35 
     36     private byte tmpBuf[] = new byte[4];
     37     private boolean isBigEndian = false;
     38 
     39     /**
     40      * Constructs a new DexData for this file.
     41      */
     42     public DexData(RandomAccessFile raf) {
     43         mDexFile = raf;
     44     }
     45 
     46     /**
     47      * Loads the contents of the DEX file into our data structures.
     48      *
     49      * @throws IOException if we encounter a problem while reading
     50      * @throws DexDataException if the DEX contents look bad
     51      */
     52     public void load() throws IOException {
     53         parseHeaderItem();
     54 
     55         loadStrings();
     56         loadTypeIds();
     57         loadProtoIds();
     58         loadFieldIds();
     59         loadMethodIds();
     60         loadClassDefs();
     61 
     62         markInternalClasses();
     63     }
     64 
     65     /**
     66      * Verifies the given magic number.
     67      */
     68     private static boolean verifyMagic(byte[] magic) {
     69         return Arrays.equals(magic, HeaderItem.DEX_FILE_MAGIC) ||
     70             Arrays.equals(magic, HeaderItem.DEX_FILE_MAGIC_API_13);
     71     }
     72 
     73     /**
     74      * Parses the interesting bits out of the header.
     75      */
     76     void parseHeaderItem() throws IOException {
     77         mHeaderItem = new HeaderItem();
     78 
     79         seek(0);
     80 
     81         byte[] magic = new byte[8];
     82         readBytes(magic);
     83         if (!verifyMagic(magic)) {
     84             System.err.println("Magic number is wrong -- are you sure " +
     85                 "this is a DEX file?");
     86             throw new DexDataException();
     87         }
     88 
     89         /*
     90          * Read the endian tag, so we properly swap things as we read
     91          * them from here on.
     92          */
     93         seek(8+4+20+4+4);
     94         mHeaderItem.endianTag = readInt();
     95         if (mHeaderItem.endianTag == HeaderItem.ENDIAN_CONSTANT) {
     96             /* do nothing */
     97         } else if (mHeaderItem.endianTag == HeaderItem.REVERSE_ENDIAN_CONSTANT){
     98             /* file is big-endian (!), reverse future reads */
     99             isBigEndian = true;
    100         } else {
    101             System.err.println("Endian constant has unexpected value " +
    102                 Integer.toHexString(mHeaderItem.endianTag));
    103             throw new DexDataException();
    104         }
    105 
    106         seek(8+4+20);  // magic, checksum, signature
    107         mHeaderItem.fileSize = readInt();
    108         mHeaderItem.headerSize = readInt();
    109         /*mHeaderItem.endianTag =*/ readInt();
    110         /*mHeaderItem.linkSize =*/ readInt();
    111         /*mHeaderItem.linkOff =*/ readInt();
    112         /*mHeaderItem.mapOff =*/ readInt();
    113         mHeaderItem.stringIdsSize = readInt();
    114         mHeaderItem.stringIdsOff = readInt();
    115         mHeaderItem.typeIdsSize = readInt();
    116         mHeaderItem.typeIdsOff = readInt();
    117         mHeaderItem.protoIdsSize = readInt();
    118         mHeaderItem.protoIdsOff = readInt();
    119         mHeaderItem.fieldIdsSize = readInt();
    120         mHeaderItem.fieldIdsOff = readInt();
    121         mHeaderItem.methodIdsSize = readInt();
    122         mHeaderItem.methodIdsOff = readInt();
    123         mHeaderItem.classDefsSize = readInt();
    124         mHeaderItem.classDefsOff = readInt();
    125         /*mHeaderItem.dataSize =*/ readInt();
    126         /*mHeaderItem.dataOff =*/ readInt();
    127     }
    128 
    129     /**
    130      * Loads the string table out of the DEX.
    131      *
    132      * First we read all of the string_id_items, then we read all of the
    133      * string_data_item.  Doing it this way should allow us to avoid
    134      * seeking around in the file.
    135      */
    136     void loadStrings() throws IOException {
    137         int count = mHeaderItem.stringIdsSize;
    138         int stringOffsets[] = new int[count];
    139 
    140         //System.out.println("reading " + count + " strings");
    141 
    142         seek(mHeaderItem.stringIdsOff);
    143         for (int i = 0; i < count; i++) {
    144             stringOffsets[i] = readInt();
    145         }
    146 
    147         mStrings = new String[count];
    148 
    149         seek(stringOffsets[0]);
    150         for (int i = 0; i < count; i++) {
    151             seek(stringOffsets[i]);         // should be a no-op
    152             mStrings[i] = readString();
    153             //System.out.println("STR: " + i + ": " + mStrings[i]);
    154         }
    155     }
    156 
    157     /**
    158      * Loads the type ID list.
    159      */
    160     void loadTypeIds() throws IOException {
    161         int count = mHeaderItem.typeIdsSize;
    162         mTypeIds = new TypeIdItem[count];
    163 
    164         //System.out.println("reading " + count + " typeIds");
    165         seek(mHeaderItem.typeIdsOff);
    166         for (int i = 0; i < count; i++) {
    167             mTypeIds[i] = new TypeIdItem();
    168             mTypeIds[i].descriptorIdx = readInt();
    169 
    170             //System.out.println(i + ": " + mTypeIds[i].descriptorIdx +
    171             //    " " + mStrings[mTypeIds[i].descriptorIdx]);
    172         }
    173     }
    174 
    175     /**
    176      * Loads the proto ID list.
    177      */
    178     void loadProtoIds() throws IOException {
    179         int count = mHeaderItem.protoIdsSize;
    180         mProtoIds = new ProtoIdItem[count];
    181 
    182         //System.out.println("reading " + count + " protoIds");
    183         seek(mHeaderItem.protoIdsOff);
    184 
    185         /*
    186          * Read the proto ID items.
    187          */
    188         for (int i = 0; i < count; i++) {
    189             mProtoIds[i] = new ProtoIdItem();
    190             mProtoIds[i].shortyIdx = readInt();
    191             mProtoIds[i].returnTypeIdx = readInt();
    192             mProtoIds[i].parametersOff = readInt();
    193 
    194             //System.out.println(i + ": " + mProtoIds[i].shortyIdx +
    195             //    " " + mStrings[mProtoIds[i].shortyIdx]);
    196         }
    197 
    198         /*
    199          * Go back through and read the type lists.
    200          */
    201         for (int i = 0; i < count; i++) {
    202             ProtoIdItem protoId = mProtoIds[i];
    203 
    204             int offset = protoId.parametersOff;
    205 
    206             if (offset == 0) {
    207                 protoId.types = new int[0];
    208                 continue;
    209             } else {
    210                 seek(offset);
    211                 int size = readInt();       // #of entries in list
    212                 protoId.types = new int[size];
    213 
    214                 for (int j = 0; j < size; j++) {
    215                     protoId.types[j] = readShort() & 0xffff;
    216                 }
    217             }
    218         }
    219     }
    220 
    221     /**
    222      * Loads the field ID list.
    223      */
    224     void loadFieldIds() throws IOException {
    225         int count = mHeaderItem.fieldIdsSize;
    226         mFieldIds = new FieldIdItem[count];
    227 
    228         //System.out.println("reading " + count + " fieldIds");
    229         seek(mHeaderItem.fieldIdsOff);
    230         for (int i = 0; i < count; i++) {
    231             mFieldIds[i] = new FieldIdItem();
    232             mFieldIds[i].classIdx = readShort() & 0xffff;
    233             mFieldIds[i].typeIdx = readShort() & 0xffff;
    234             mFieldIds[i].nameIdx = readInt();
    235 
    236             //System.out.println(i + ": " + mFieldIds[i].nameIdx +
    237             //    " " + mStrings[mFieldIds[i].nameIdx]);
    238         }
    239     }
    240 
    241     /**
    242      * Loads the method ID list.
    243      */
    244     void loadMethodIds() throws IOException {
    245         int count = mHeaderItem.methodIdsSize;
    246         mMethodIds = new MethodIdItem[count];
    247 
    248         //System.out.println("reading " + count + " methodIds");
    249         seek(mHeaderItem.methodIdsOff);
    250         for (int i = 0; i < count; i++) {
    251             mMethodIds[i] = new MethodIdItem();
    252             mMethodIds[i].classIdx = readShort() & 0xffff;
    253             mMethodIds[i].protoIdx = readShort() & 0xffff;
    254             mMethodIds[i].nameIdx = readInt();
    255 
    256             //System.out.println(i + ": " + mMethodIds[i].nameIdx +
    257             //    " " + mStrings[mMethodIds[i].nameIdx]);
    258         }
    259     }
    260 
    261     /**
    262      * Loads the class defs list.
    263      */
    264     void loadClassDefs() throws IOException {
    265         int count = mHeaderItem.classDefsSize;
    266         mClassDefs = new ClassDefItem[count];
    267 
    268         //System.out.println("reading " + count + " classDefs");
    269         seek(mHeaderItem.classDefsOff);
    270         for (int i = 0; i < count; i++) {
    271             mClassDefs[i] = new ClassDefItem();
    272             mClassDefs[i].classIdx = readInt();
    273 
    274             /* access_flags = */ readInt();
    275             /* superclass_idx = */ readInt();
    276             /* interfaces_off = */ readInt();
    277             /* source_file_idx = */ readInt();
    278             /* annotations_off = */ readInt();
    279             /* class_data_off = */ readInt();
    280             /* static_values_off = */ readInt();
    281 
    282             //System.out.println(i + ": " + mClassDefs[i].classIdx + " " +
    283             //    mStrings[mTypeIds[mClassDefs[i].classIdx].descriptorIdx]);
    284         }
    285     }
    286 
    287     /**
    288      * Sets the "internal" flag on type IDs which are defined in the
    289      * DEX file or within the VM (e.g. primitive classes and arrays).
    290      */
    291     void markInternalClasses() {
    292         for (int i = mClassDefs.length -1; i >= 0; i--) {
    293             mTypeIds[mClassDefs[i].classIdx].internal = true;
    294         }
    295 
    296         for (int i = 0; i < mTypeIds.length; i++) {
    297             String className = mStrings[mTypeIds[i].descriptorIdx];
    298 
    299             if (className.length() == 1) {
    300                 // primitive class
    301                 mTypeIds[i].internal = true;
    302             } else if (className.charAt(0) == '[') {
    303                 mTypeIds[i].internal = true;
    304             }
    305 
    306             //System.out.println(i + " " +
    307             //    (mTypeIds[i].internal ? "INTERNAL" : "external") + " - " +
    308             //    mStrings[mTypeIds[i].descriptorIdx]);
    309         }
    310     }
    311 
    312 
    313     /*
    314      * =======================================================================
    315      *      Queries
    316      * =======================================================================
    317      */
    318 
    319     /**
    320      * Returns the class name, given an index into the type_ids table.
    321      */
    322     private String classNameFromTypeIndex(int idx) {
    323         return mStrings[mTypeIds[idx].descriptorIdx];
    324     }
    325 
    326     /**
    327      * Returns an array of method argument type strings, given an index
    328      * into the proto_ids table.
    329      */
    330     private String[] argArrayFromProtoIndex(int idx) {
    331         ProtoIdItem protoId = mProtoIds[idx];
    332         String[] result = new String[protoId.types.length];
    333 
    334         for (int i = 0; i < protoId.types.length; i++) {
    335             result[i] = mStrings[mTypeIds[protoId.types[i]].descriptorIdx];
    336         }
    337 
    338         return result;
    339     }
    340 
    341     /**
    342      * Returns a string representing the method's return type, given an
    343      * index into the proto_ids table.
    344      */
    345     private String returnTypeFromProtoIndex(int idx) {
    346         ProtoIdItem protoId = mProtoIds[idx];
    347         return mStrings[mTypeIds[protoId.returnTypeIdx].descriptorIdx];
    348     }
    349 
    350     /**
    351      * Returns an array with all of the class references that don't
    352      * correspond to classes in the DEX file.  Each class reference has
    353      * a list of the referenced fields and methods associated with
    354      * that class.
    355      */
    356     public ClassRef[] getExternalReferences() {
    357         // create a sparse array of ClassRef that parallels mTypeIds
    358         ClassRef[] sparseRefs = new ClassRef[mTypeIds.length];
    359 
    360         // create entries for all externally-referenced classes
    361         int count = 0;
    362         for (int i = 0; i < mTypeIds.length; i++) {
    363             if (!mTypeIds[i].internal) {
    364                 sparseRefs[i] =
    365                     new ClassRef(mStrings[mTypeIds[i].descriptorIdx]);
    366                 count++;
    367             }
    368         }
    369 
    370         // add fields and methods to the appropriate class entry
    371         addExternalFieldReferences(sparseRefs);
    372         addExternalMethodReferences(sparseRefs);
    373 
    374         // crunch out the sparseness
    375         ClassRef[] classRefs = new ClassRef[count];
    376         int idx = 0;
    377         for (int i = 0; i < mTypeIds.length; i++) {
    378             if (sparseRefs[i] != null)
    379                 classRefs[idx++] = sparseRefs[i];
    380         }
    381 
    382         assert idx == count;
    383 
    384         return classRefs;
    385     }
    386 
    387     /**
    388      * Runs through the list of field references, inserting external
    389      * references into the appropriate ClassRef.
    390      */
    391     private void addExternalFieldReferences(ClassRef[] sparseRefs) {
    392         for (int i = 0; i < mFieldIds.length; i++) {
    393             if (!mTypeIds[mFieldIds[i].classIdx].internal) {
    394                 FieldIdItem fieldId = mFieldIds[i];
    395                 FieldRef newFieldRef = new FieldRef(
    396                         classNameFromTypeIndex(fieldId.classIdx),
    397                         classNameFromTypeIndex(fieldId.typeIdx),
    398                         mStrings[fieldId.nameIdx]);
    399                 sparseRefs[mFieldIds[i].classIdx].addField(newFieldRef);
    400             }
    401         }
    402     }
    403 
    404     /**
    405      * Runs through the list of method references, inserting external
    406      * references into the appropriate ClassRef.
    407      */
    408     private void addExternalMethodReferences(ClassRef[] sparseRefs) {
    409         for (int i = 0; i < mMethodIds.length; i++) {
    410             if (!mTypeIds[mMethodIds[i].classIdx].internal) {
    411                 MethodIdItem methodId = mMethodIds[i];
    412                 MethodRef newMethodRef = new MethodRef(
    413                         classNameFromTypeIndex(methodId.classIdx),
    414                         argArrayFromProtoIndex(methodId.protoIdx),
    415                         returnTypeFromProtoIndex(methodId.protoIdx),
    416                         mStrings[methodId.nameIdx]);
    417                 sparseRefs[mMethodIds[i].classIdx].addMethod(newMethodRef);
    418             }
    419         }
    420     }
    421 
    422 
    423     /*
    424      * =======================================================================
    425      *      Basic I/O functions
    426      * =======================================================================
    427      */
    428 
    429     /**
    430      * Seeks the DEX file to the specified absolute position.
    431      */
    432     void seek(int position) throws IOException {
    433         mDexFile.seek(position);
    434     }
    435 
    436     /**
    437      * Fills the buffer by reading bytes from the DEX file.
    438      */
    439     void readBytes(byte[] buffer) throws IOException {
    440         mDexFile.readFully(buffer);
    441     }
    442 
    443     /**
    444      * Reads a single signed byte value.
    445      */
    446     byte readByte() throws IOException {
    447         mDexFile.readFully(tmpBuf, 0, 1);
    448         return tmpBuf[0];
    449     }
    450 
    451     /**
    452      * Reads a signed 16-bit integer, byte-swapping if necessary.
    453      */
    454     short readShort() throws IOException {
    455         mDexFile.readFully(tmpBuf, 0, 2);
    456         if (isBigEndian) {
    457             return (short) ((tmpBuf[1] & 0xff) | ((tmpBuf[0] & 0xff) << 8));
    458         } else {
    459             return (short) ((tmpBuf[0] & 0xff) | ((tmpBuf[1] & 0xff) << 8));
    460         }
    461     }
    462 
    463     /**
    464      * Reads a signed 32-bit integer, byte-swapping if necessary.
    465      */
    466     int readInt() throws IOException {
    467         mDexFile.readFully(tmpBuf, 0, 4);
    468 
    469         if (isBigEndian) {
    470             return (tmpBuf[3] & 0xff) | ((tmpBuf[2] & 0xff) << 8) |
    471                    ((tmpBuf[1] & 0xff) << 16) | ((tmpBuf[0] & 0xff) << 24);
    472         } else {
    473             return (tmpBuf[0] & 0xff) | ((tmpBuf[1] & 0xff) << 8) |
    474                    ((tmpBuf[2] & 0xff) << 16) | ((tmpBuf[3] & 0xff) << 24);
    475         }
    476     }
    477 
    478     /**
    479      * Reads a variable-length unsigned LEB128 value.  Does not attempt to
    480      * verify that the value is valid.
    481      *
    482      * @throws EOFException if we run off the end of the file
    483      */
    484     int readUnsignedLeb128() throws IOException {
    485         int result = 0;
    486         byte val;
    487 
    488         do {
    489             val = readByte();
    490             result = (result << 7) | (val & 0x7f);
    491         } while (val < 0);
    492 
    493         return result;
    494     }
    495 
    496     /**
    497      * Reads a UTF-8 string.
    498      *
    499      * We don't know how long the UTF-8 string is, so we have to read one
    500      * byte at a time.  We could make an educated guess based on the
    501      * utf16_size and seek back if we get it wrong, but seeking backward
    502      * may cause the underlying implementation to reload I/O buffers.
    503      */
    504     String readString() throws IOException {
    505         int utf16len = readUnsignedLeb128();
    506         byte inBuf[] = new byte[utf16len * 3];      // worst case
    507         int idx;
    508 
    509         for (idx = 0; idx < inBuf.length; idx++) {
    510             byte val = readByte();
    511             if (val == 0)
    512                 break;
    513             inBuf[idx] = val;
    514         }
    515 
    516         return new String(inBuf, 0, idx, "UTF-8");
    517     }
    518 
    519 
    520     /*
    521      * =======================================================================
    522      *      Internal "structure" declarations
    523      * =======================================================================
    524      */
    525 
    526     /**
    527      * Holds the contents of a header_item.
    528      */
    529     static class HeaderItem {
    530         public int fileSize;
    531         public int headerSize;
    532         public int endianTag;
    533         public int stringIdsSize, stringIdsOff;
    534         public int typeIdsSize, typeIdsOff;
    535         public int protoIdsSize, protoIdsOff;
    536         public int fieldIdsSize, fieldIdsOff;
    537         public int methodIdsSize, methodIdsOff;
    538         public int classDefsSize, classDefsOff;
    539 
    540         /* expected magic values */
    541         public static final byte[] DEX_FILE_MAGIC = {
    542             0x64, 0x65, 0x78, 0x0a, 0x30, 0x33, 0x36, 0x00 };
    543         public static final byte[] DEX_FILE_MAGIC_API_13 = {
    544             0x64, 0x65, 0x78, 0x0a, 0x30, 0x33, 0x35, 0x00 };
    545         public static final int ENDIAN_CONSTANT = 0x12345678;
    546         public static final int REVERSE_ENDIAN_CONSTANT = 0x78563412;
    547     }
    548 
    549     /**
    550      * Holds the contents of a type_id_item.
    551      *
    552      * This is chiefly a list of indices into the string table.  We need
    553      * some additional bits of data, such as whether or not the type ID
    554      * represents a class defined in this DEX, so we use an object for
    555      * each instead of a simple integer.  (Could use a parallel array, but
    556      * since this is a desktop app it's not essential.)
    557      */
    558     static class TypeIdItem {
    559         public int descriptorIdx;       // index into string_ids
    560 
    561         public boolean internal;        // defined within this DEX file?
    562     }
    563 
    564     /**
    565      * Holds the contents of a proto_id_item.
    566      */
    567     static class ProtoIdItem {
    568         public int shortyIdx;           // index into string_ids
    569         public int returnTypeIdx;       // index into type_ids
    570         public int parametersOff;       // file offset to a type_list
    571 
    572         public int types[];             // contents of type list
    573     }
    574 
    575     /**
    576      * Holds the contents of a field_id_item.
    577      */
    578     static class FieldIdItem {
    579         public int classIdx;            // index into type_ids (defining class)
    580         public int typeIdx;             // index into type_ids (field type)
    581         public int nameIdx;             // index into string_ids
    582     }
    583 
    584     /**
    585      * Holds the contents of a method_id_item.
    586      */
    587     static class MethodIdItem {
    588         public int classIdx;            // index into type_ids
    589         public int protoIdx;            // index into proto_ids
    590         public int nameIdx;             // index into string_ids
    591     }
    592 
    593     /**
    594      * Holds the contents of a class_def_item.
    595      *
    596      * We don't really need a class for this, but there's some stuff in
    597      * the class_def_item that we might want later.
    598      */
    599     static class ClassDefItem {
    600         public int classIdx;            // index into type_ids
    601     }
    602 }
    603