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      */
DexData(RandomAccessFile raf)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      */
load()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      */
verifyMagic(byte[] magic)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      */
parseHeaderItem()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      */
loadStrings()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      */
loadTypeIds()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      */
loadProtoIds()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      */
loadFieldIds()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      */
loadMethodIds()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      */
loadClassDefs()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      */
markInternalClasses()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      */
classNameFromTypeIndex(int idx)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      */
argArrayFromProtoIndex(int idx)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      */
returnTypeFromProtoIndex(int idx)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      */
getExternalReferences()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      */
addExternalFieldReferences(ClassRef[] sparseRefs)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      */
addExternalMethodReferences(ClassRef[] sparseRefs)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      */
seek(int position)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      */
readBytes(byte[] buffer)439     void readBytes(byte[] buffer) throws IOException {
440         mDexFile.readFully(buffer);
441     }
442 
443     /**
444      * Reads a single signed byte value.
445      */
readByte()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      */
readShort()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      */
readInt()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      */
readUnsignedLeb128()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      */
readString()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