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
17package com.android.dexdeps;
18
19import java.io.IOException;
20import java.io.RandomAccessFile;
21import java.util.Arrays;
22
23/**
24 * Data extracted from a DEX file.
25 */
26public 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