• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.ReferenceType;
37 import org.jf.dexlib2.dexbacked.raw.*;
38 import org.jf.dexlib2.dexbacked.reference.DexBackedFieldReference;
39 import org.jf.dexlib2.dexbacked.reference.DexBackedMethodReference;
40 import org.jf.dexlib2.dexbacked.reference.DexBackedStringReference;
41 import org.jf.dexlib2.dexbacked.reference.DexBackedTypeReference;
42 import org.jf.dexlib2.dexbacked.util.FixedSizeSet;
43 import org.jf.dexlib2.iface.DexFile;
44 import org.jf.dexlib2.iface.reference.Reference;
45 import org.jf.dexlib2.util.DexUtil;
46 import org.jf.util.ExceptionWithContext;
47 
48 import javax.annotation.Nonnull;
49 import javax.annotation.Nullable;
50 import java.io.IOException;
51 import java.io.InputStream;
52 import java.util.AbstractList;
53 import java.util.List;
54 import java.util.Set;
55 
56 public class DexBackedDexFile extends BaseDexBuffer implements DexFile {
57     @Nonnull private final Opcodes opcodes;
58 
59     private final int stringCount;
60     private final int stringStartOffset;
61     private final int typeCount;
62     private final int typeStartOffset;
63     private final int protoCount;
64     private final int protoStartOffset;
65     private final int fieldCount;
66     private final int fieldStartOffset;
67     private final int methodCount;
68     private final int methodStartOffset;
69     private final int classCount;
70     private final int classStartOffset;
71 
DexBackedDexFile(@onnull Opcodes opcodes, @Nonnull byte[] buf, int offset, boolean verifyMagic)72     protected DexBackedDexFile(@Nonnull Opcodes opcodes, @Nonnull byte[] buf, int offset, boolean verifyMagic) {
73         super(buf, offset);
74 
75         this.opcodes = opcodes;
76 
77         if (verifyMagic) {
78             DexUtil.verifyDexHeader(buf, offset);
79         }
80 
81         stringCount = readSmallUint(HeaderItem.STRING_COUNT_OFFSET);
82         stringStartOffset = readSmallUint(HeaderItem.STRING_START_OFFSET);
83         typeCount = readSmallUint(HeaderItem.TYPE_COUNT_OFFSET);
84         typeStartOffset = readSmallUint(HeaderItem.TYPE_START_OFFSET);
85         protoCount = readSmallUint(HeaderItem.PROTO_COUNT_OFFSET);
86         protoStartOffset = readSmallUint(HeaderItem.PROTO_START_OFFSET);
87         fieldCount = readSmallUint(HeaderItem.FIELD_COUNT_OFFSET);
88         fieldStartOffset = readSmallUint(HeaderItem.FIELD_START_OFFSET);
89         methodCount = readSmallUint(HeaderItem.METHOD_COUNT_OFFSET);
90         methodStartOffset = readSmallUint(HeaderItem.METHOD_START_OFFSET);
91         classCount = readSmallUint(HeaderItem.CLASS_COUNT_OFFSET);
92         classStartOffset = readSmallUint(HeaderItem.CLASS_START_OFFSET);
93     }
94 
DexBackedDexFile(@onnull Opcodes opcodes, @Nonnull BaseDexBuffer buf)95     public DexBackedDexFile(@Nonnull Opcodes opcodes, @Nonnull BaseDexBuffer buf) {
96         this(opcodes, buf.buf, buf.baseOffset);
97     }
98 
DexBackedDexFile(@onnull Opcodes opcodes, @Nonnull byte[] buf, int offset)99     public DexBackedDexFile(@Nonnull Opcodes opcodes, @Nonnull byte[] buf, int offset) {
100         this(opcodes, buf, offset, false);
101     }
102 
DexBackedDexFile(@onnull Opcodes opcodes, @Nonnull byte[] buf)103     public DexBackedDexFile(@Nonnull Opcodes opcodes, @Nonnull byte[] buf) {
104         this(opcodes, buf, 0, true);
105     }
106 
107     @Nonnull
fromInputStream(@onnull Opcodes opcodes, @Nonnull InputStream is)108     public static DexBackedDexFile fromInputStream(@Nonnull Opcodes opcodes, @Nonnull InputStream is)
109             throws IOException {
110         DexUtil.verifyDexHeader(is);
111 
112         byte[] buf = ByteStreams.toByteArray(is);
113         return new DexBackedDexFile(opcodes, buf, 0, false);
114     }
115 
getOpcodes()116     @Override @Nonnull public Opcodes getOpcodes() {
117         return opcodes;
118     }
119 
120     // Will only be true for a dalvik-style odex file
isOdexFile()121     public boolean isOdexFile() {
122         return false;
123     }
124 
125     // Will be true for both a dalvik-style odex file, and an art-style odex file embedded in an oat file
hasOdexOpcodes()126     public boolean hasOdexOpcodes() {
127         return false;
128     }
129 
130     @Nonnull
131     @Override
getClasses()132     public Set<? extends DexBackedClassDef> getClasses() {
133         return new FixedSizeSet<DexBackedClassDef>() {
134             @Nonnull
135             @Override
136             public DexBackedClassDef readItem(int index) {
137                 return new DexBackedClassDef(DexBackedDexFile.this, getClassDefItemOffset(index));
138             }
139 
140             @Override
141             public int size() {
142                 return classCount;
143             }
144         };
145     }
146 
147     public int getStringIdItemOffset(int stringIndex) {
148         if (stringIndex < 0 || stringIndex >= stringCount) {
149             throw new InvalidItemIndex(stringIndex, "String index out of bounds: %d", stringIndex);
150         }
151         return stringStartOffset + stringIndex*StringIdItem.ITEM_SIZE;
152     }
153 
154     public int getTypeIdItemOffset(int typeIndex) {
155         if (typeIndex < 0 || typeIndex >= typeCount) {
156             throw new InvalidItemIndex(typeIndex, "Type index out of bounds: %d", typeIndex);
157         }
158         return typeStartOffset + typeIndex*TypeIdItem.ITEM_SIZE;
159     }
160 
161     public int getFieldIdItemOffset(int fieldIndex) {
162         if (fieldIndex < 0 || fieldIndex >= fieldCount) {
163             throw new InvalidItemIndex(fieldIndex, "Field index out of bounds: %d", fieldIndex);
164         }
165         return fieldStartOffset + fieldIndex*FieldIdItem.ITEM_SIZE;
166     }
167 
168     public int getMethodIdItemOffset(int methodIndex) {
169         if (methodIndex < 0 || methodIndex >= methodCount) {
170             throw new InvalidItemIndex(methodIndex, "Method index out of bounds: %d", methodIndex);
171         }
172         return methodStartOffset + methodIndex*MethodIdItem.ITEM_SIZE;
173     }
174 
175     public int getProtoIdItemOffset(int protoIndex) {
176         if (protoIndex < 0 || protoIndex >= protoCount) {
177             throw new InvalidItemIndex(protoIndex, "Proto index out of bounds: %d", protoIndex);
178         }
179         return protoStartOffset + protoIndex*ProtoIdItem.ITEM_SIZE;
180     }
181 
182     public int getClassDefItemOffset(int classIndex) {
183         if (classIndex < 0 || classIndex >= classCount) {
184             throw new InvalidItemIndex(classIndex, "Class index out of bounds: %d", classIndex);
185         }
186         return classStartOffset + classIndex*ClassDefItem.ITEM_SIZE;
187     }
188 
189     public int getClassCount() {
190         return classCount;
191     }
192 
193     public int getStringCount() {
194         return stringCount;
195     }
196 
197     public int getTypeCount() {
198         return typeCount;
199     }
200 
201     public int getProtoCount() {
202         return protoCount;
203     }
204 
205     public int getFieldCount() {
206         return fieldCount;
207     }
208 
209     public int getMethodCount() {
210         return methodCount;
211     }
212 
213     @Nonnull
214     public String getString(int stringIndex) {
215         int stringOffset = getStringIdItemOffset(stringIndex);
216         int stringDataOffset = readSmallUint(stringOffset);
217         DexReader reader = readerAt(stringDataOffset);
218         int utf16Length = reader.readSmallUleb128();
219         return reader.readString(utf16Length);
220     }
221 
222     @Nullable
223     public String getOptionalString(int stringIndex) {
224         if (stringIndex == -1) {
225             return null;
226         }
227         return getString(stringIndex);
228     }
229 
230     @Nonnull
231     public String getType(int typeIndex) {
232         int typeOffset = getTypeIdItemOffset(typeIndex);
233         int stringIndex = readSmallUint(typeOffset);
234         return getString(stringIndex);
235     }
236 
237     @Nullable
238     public String getOptionalType(int typeIndex) {
239         if (typeIndex == -1) {
240             return null;
241         }
242         return getType(typeIndex);
243     }
244 
245     public List<DexBackedStringReference> getStrings() {
246         return new AbstractList<DexBackedStringReference>() {
247             @Override public DexBackedStringReference get(int index) {
248                 if (index < 0 || index >= getStringCount()) {
249                     throw new IndexOutOfBoundsException();
250                 }
251                 return new DexBackedStringReference(DexBackedDexFile.this, index);
252             }
253 
254             @Override public int size() {
255                 return getStringCount();
256             }
257         };
258     }
259 
260     public List<DexBackedTypeReference> getTypes() {
261         return new AbstractList<DexBackedTypeReference>() {
262             @Override public DexBackedTypeReference get(int index) {
263                 if (index < 0 || index >= getTypeCount()) {
264                     throw new IndexOutOfBoundsException();
265                 }
266                 return new DexBackedTypeReference(DexBackedDexFile.this, index);
267             }
268 
269             @Override public int size() {
270                 return getTypeCount();
271             }
272         };
273     }
274 
275     public List<DexBackedMethodReference> getMethods() {
276         return new AbstractList<DexBackedMethodReference>() {
277             @Override public DexBackedMethodReference get(int index) {
278                 if (index < 0 || index >= getMethodCount()) {
279                     throw new IndexOutOfBoundsException();
280                 }
281                 return new DexBackedMethodReference(DexBackedDexFile.this, index);
282             }
283 
284             @Override public int size() {
285                 return getMethodCount();
286             }
287         };
288     }
289 
290     public List<DexBackedFieldReference> getFields() {
291         return new AbstractList<DexBackedFieldReference>() {
292             @Override public DexBackedFieldReference get(int index) {
293                 if (index < 0 || index >= getFieldCount()) {
294                     throw new IndexOutOfBoundsException();
295                 }
296                 return new DexBackedFieldReference(DexBackedDexFile.this, index);
297             }
298 
299             @Override public int size() {
300                 return getFieldCount();
301             }
302         };
303     }
304 
305     public List<? extends Reference> getReferences(int referenceType) {
306         switch (referenceType) {
307             case ReferenceType.STRING:
308                 return getStrings();
309             case ReferenceType.TYPE:
310                 return getTypes();
311             case ReferenceType.METHOD:
312                 return getMethods();
313             case ReferenceType.FIELD:
314                 return getFields();
315             default:
316                 throw new IllegalArgumentException(String.format("Invalid reference type: %d", referenceType));
317         }
318     }
319 
320     @Override
321     @Nonnull
322     public DexReader readerAt(int offset) {
323         return new DexReader(this, offset);
324     }
325 
326     public static class NotADexFile extends RuntimeException {
327         public NotADexFile() {
328         }
329 
330         public NotADexFile(Throwable cause) {
331             super(cause);
332         }
333 
334         public NotADexFile(String message) {
335             super(message);
336         }
337 
338         public NotADexFile(String message, Throwable cause) {
339             super(message, cause);
340         }
341     }
342 
343     public static class InvalidItemIndex extends ExceptionWithContext {
344         private final int itemIndex;
345 
346         public InvalidItemIndex(int itemIndex) {
347             super("");
348             this.itemIndex = itemIndex;
349         }
350 
351         public InvalidItemIndex(int itemIndex, String message, Object... formatArgs) {
352             super(message, formatArgs);
353             this.itemIndex = itemIndex;
354         }
355 
356         public int getInvalidIndex() {
357             return itemIndex;
358         }
359     }
360 }
361