DexBackedDexFile.java revision df443569f2c10b2cc3067e4fd98ca7388a956dd6
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
32package org.jf.dexlib2.dexbacked;
33
34import org.jf.dexlib2.dexbacked.raw.HeaderItem;
35import org.jf.dexlib2.dexbacked.raw.StringIdItem;
36import org.jf.dexlib2.dexbacked.raw.TypeIdItem;
37import org.jf.dexlib2.dexbacked.util.FixedSizeSet;
38import org.jf.dexlib2.iface.DexFile;
39import org.jf.dexlib2.util.AnnotatedBytes;
40import org.jf.util.ExceptionWithContext;
41import org.jf.util.Utf8Utils;
42
43import javax.annotation.Nonnull;
44import javax.annotation.Nullable;
45import java.io.IOException;
46import java.io.Writer;
47import java.util.Set;
48
49public abstract class DexBackedDexFile extends BaseDexBuffer implements DexFile {
50    public DexBackedDexFile(@Nonnull byte[] buf) {
51        super(buf);
52    }
53
54    @Nonnull public abstract String getString(int stringIndex);
55    @Nullable public abstract String getOptionalString(int stringIndex);
56    @Nonnull public abstract String getType(int typeIndex);
57    @Nullable public abstract String getOptionalType(int typeIndex);
58
59    // TODO: refactor how dex items are read
60    public abstract int getMethodIdItemOffset(int methodIndex);
61    public abstract int getProtoIdItemOffset(int protoIndex);
62    public abstract int getFieldIdItemOffset(int fieldIndex);
63
64    @Override @Nonnull public abstract DexReader readerAt(int offset);
65
66    public abstract void dumpTo(Writer out, int width) throws IOException;
67
68    public static class Impl extends DexBackedDexFile {
69        private final int stringCount;
70        private final int stringStartOffset;
71        private final int typeCount;
72        private final int typeStartOffset;
73        private final int protoCount;
74        private final int protoStartOffset;
75        private final int fieldCount;
76        private final int fieldStartOffset;
77        private final int methodCount;
78        private final int methodStartOffset;
79        private final int classCount;
80        private final int classStartOffset;
81
82        private static final int PROTO_ID_ITEM_SIZE = 12;
83        private static final int FIELD_ID_ITEM_SIZE = 8;
84        private static final int METHOD_ID_ITEM_SIZE = 8;
85        private static final int CLASS_DEF_ITEM_SIZE = 32;
86        public static final int MAP_ITEM_SIZE = 12;
87
88        public static final int FIELD_CLASS_IDX_OFFSET = 0;
89        public static final int FIELD_TYPE_IDX_OFFSET = 2;
90        public static final int FIELD_NAME_IDX_OFFSET = 4;
91
92        public static final int METHOD_CLASS_IDX_OFFSET = 0;
93        public static final int METHOD_PROTO_IDX_OFFSET = 2;
94        public static final int METHOD_NAME_IDX_OFFSET = 4;
95
96        public static final int PROTO_RETURN_TYPE_IDX_OFFSET = 4;
97        public static final int PROTO_PARAM_LIST_OFF_OFFSET = 8;
98
99        public static final int TYPE_LIST_SIZE_OFFSET = 0;
100        public static final int TYPE_LIST_LIST_OFFSET = 4;
101
102        public Impl(@Nonnull byte[] buf) {
103            super(buf);
104
105            verifyMagic();
106            verifyEndian();
107            stringCount = readSmallUint(HeaderItem.STRING_COUNT_OFFSET);
108            stringStartOffset = readSmallUint(HeaderItem.STRING_START_OFFSET);
109            typeCount = readSmallUint(HeaderItem.TYPE_COUNT_OFFSET);
110            typeStartOffset = readSmallUint(HeaderItem.TYPE_START_OFFSET);
111            protoCount = readSmallUint(HeaderItem.PROTO_COUNT_OFFSET);
112            protoStartOffset = readSmallUint(HeaderItem.PROTO_START_OFFSET);
113            fieldCount = readSmallUint(HeaderItem.FIELD_COUNT_OFFSET);
114            fieldStartOffset = readSmallUint(HeaderItem.FIELD_START_OFFSET);
115            methodCount = readSmallUint(HeaderItem.METHOD_COUNT_OFFSET);
116            methodStartOffset = readSmallUint(HeaderItem.METHOD_START_OFFSET);
117            classCount = readSmallUint(HeaderItem.CLASS_COUNT_OFFSET);
118            classStartOffset = readSmallUint(HeaderItem.CLASS_START_OFFSET);
119        }
120
121        @Nonnull
122        @Override
123        public Set<? extends DexBackedClassDef> getClasses() {
124            return new FixedSizeSet<DexBackedClassDef>() {
125                @Nonnull
126                @Override
127                public DexBackedClassDef readItem(int index) {
128                    return new DexBackedClassDef(Impl.this, getClassDefItemOffset(index));
129                }
130
131                @Override
132                public int size() {
133                    return classCount;
134                }
135            };
136        }
137
138        private void verifyMagic() {
139            outer: for (byte[] magic: HeaderItem.MAGIC_VALUES) {
140                for (int i=0; i<magic.length; i++) {
141                    if (buf[i] != magic[i]) {
142                        continue outer;
143                    }
144                }
145                return;
146            }
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 ExceptionWithContext(sb.toString());
152        }
153
154        private void verifyEndian() {
155            int endian = readInt(HeaderItem.ENDIAN_TAG_OFFSET);
156            if (endian == HeaderItem.BIG_ENDIAN_TAG) {
157                throw new ExceptionWithContext("dexlib does not currently support big endian dex files.");
158            } else if (endian != HeaderItem.LITTLE_ENDIAN_TAG) {
159                StringBuilder sb = new StringBuilder("Invalid endian tag:");
160                for (int i=0; i<4; i++) {
161                    sb.append(String.format(" %02x", buf[HeaderItem.ENDIAN_TAG_OFFSET+i]));
162                }
163                throw new ExceptionWithContext(sb.toString());
164            }
165        }
166
167        public int getStringIdItemOffset(int stringIndex) {
168            if (stringIndex < 0 || stringIndex >= stringCount) {
169                throw new ExceptionWithContext("String index out of bounds: %d", stringIndex);
170            }
171            return stringStartOffset + stringIndex*StringIdItem.ITEM_SIZE;
172        }
173
174        public int getTypeIdItemOffset(int typeIndex) {
175            if (typeIndex < 0 || typeIndex >= typeCount) {
176                throw new ExceptionWithContext("Type index out of bounds: %d", typeIndex);
177            }
178            return typeStartOffset + typeIndex*TypeIdItem.ITEM_SIZE;
179        }
180
181        @Override
182        public int getFieldIdItemOffset(int fieldIndex) {
183            if (fieldIndex < 0 || fieldIndex >= fieldCount) {
184                throw new ExceptionWithContext("Field index out of bounds: %d", fieldIndex);
185            }
186            return fieldStartOffset + fieldIndex*FIELD_ID_ITEM_SIZE;
187        }
188
189        @Override
190        public int getMethodIdItemOffset(int methodIndex) {
191            if (methodIndex < 0 || methodIndex >= methodCount) {
192                throw new ExceptionWithContext("Method index out of bounds: %d", methodIndex);
193            }
194            return methodStartOffset + methodIndex*METHOD_ID_ITEM_SIZE;
195        }
196
197        @Override
198        public int getProtoIdItemOffset(int protoIndex) {
199            if (protoIndex < 0 || protoIndex >= protoCount) {
200                throw new ExceptionWithContext("Proto index out of bounds: %d", protoIndex);
201            }
202            return protoStartOffset + protoIndex*PROTO_ID_ITEM_SIZE;
203        }
204
205        public int getClassDefItemOffset(int classIndex) {
206            if (classIndex < 0 || classIndex >= classCount) {
207                throw new ExceptionWithContext("Class index out of bounds: %d", classIndex);
208            }
209            return classStartOffset + classIndex*CLASS_DEF_ITEM_SIZE;
210        }
211
212        public int getClassCount() {
213            return classCount;
214        }
215
216        @Override
217        @Nonnull
218        public String getString(int stringIndex) {
219            int stringOffset = getStringIdItemOffset(stringIndex);
220            int stringDataOffset = readSmallUint(stringOffset);
221            DexReader reader = readerAt(stringDataOffset);
222            int utf16Length = reader.readSmallUleb128();
223            return Utf8Utils.utf8BytesWithUtf16LengthToString(buf, reader.getOffset(), utf16Length);
224        }
225
226        @Override
227        @Nullable
228        public String getOptionalString(int stringIndex) {
229            if (stringIndex == -1) {
230                return null;
231            }
232            return getString(stringIndex);
233        }
234
235        @Override
236        @Nonnull
237        public String getType(int typeIndex) {
238            int typeOffset = getTypeIdItemOffset(typeIndex);
239            int stringIndex = readSmallUint(typeOffset);
240            return getString(stringIndex);
241        }
242
243        @Override
244        @Nullable
245        public String getOptionalType(int typeIndex) {
246            if (typeIndex == -1) {
247                return null;
248            }
249            return getType(typeIndex);
250        }
251
252        @Override
253        @Nonnull
254        public DexReader readerAt(int offset) {
255            return new DexReader(this, offset);
256        }
257
258        public void dumpTo(Writer out, int width) throws IOException {
259            AnnotatedBytes annotatedBytes = new AnnotatedBytes(width);
260            HeaderItem.getAnnotator().annotateSection(annotatedBytes, this, 1);
261
262            annotatedBytes.annotate(0, " ");
263            StringIdItem.getAnnotator().annotateSection(annotatedBytes, this, stringCount);
264
265            annotatedBytes.annotate(0, " ");
266            TypeIdItem.getAnnotator().annotateSection(annotatedBytes, this, typeCount);
267
268            annotatedBytes.writeAnnotations(out, buf);
269        }
270    }
271}
272