• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 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.dx.io;
18 
19 import com.android.dx.dex.DexFormat;
20 import com.android.dx.dex.SizeOf;
21 import com.android.dx.dex.TableOfContents;
22 import com.android.dx.merge.TypeList;
23 import com.android.dx.util.ByteInput;
24 import com.android.dx.util.ByteOutput;
25 import com.android.dx.util.DexException;
26 import com.android.dx.util.FileUtils;
27 import com.android.dx.util.Leb128Utils;
28 import com.android.dx.util.Mutf8;
29 import java.io.ByteArrayOutputStream;
30 import java.io.File;
31 import java.io.FileInputStream;
32 import java.io.FileOutputStream;
33 import java.io.IOException;
34 import java.io.InputStream;
35 import java.io.OutputStream;
36 import java.io.UTFDataFormatException;
37 import java.util.AbstractList;
38 import java.util.Arrays;
39 import java.util.Collections;
40 import java.util.Iterator;
41 import java.util.List;
42 import java.util.NoSuchElementException;
43 import java.util.zip.ZipEntry;
44 import java.util.zip.ZipFile;
45 
46 /**
47  * The bytes of a dex file in memory for reading and writing. All int offsets
48  * are unsigned.
49  */
50 public final class DexBuffer {
51     private byte[] data;
52     private final TableOfContents tableOfContents = new TableOfContents();
53     private int length = 0;
54 
55     private final List<String> strings = new AbstractList<String>() {
56         @Override public String get(int index) {
57             checkBounds(index, tableOfContents.stringIds.size);
58             return open(tableOfContents.stringIds.off + (index * SizeOf.STRING_ID_ITEM))
59                     .readString();
60         }
61         @Override public int size() {
62             return tableOfContents.stringIds.size;
63         }
64     };
65 
66     private final List<Integer> typeIds = new AbstractList<Integer>() {
67         @Override public Integer get(int index) {
68             checkBounds(index, tableOfContents.typeIds.size);
69             return open(tableOfContents.typeIds.off + (index * SizeOf.TYPE_ID_ITEM)).readInt();
70         }
71         @Override public int size() {
72             return tableOfContents.typeIds.size;
73         }
74     };
75 
76     private final List<String> typeNames = new AbstractList<String>() {
77         @Override public String get(int index) {
78             checkBounds(index, tableOfContents.typeIds.size);
79             return strings.get(typeIds.get(index));
80         }
81         @Override public int size() {
82             return tableOfContents.typeIds.size;
83         }
84     };
85 
86     private final List<ProtoId> protoIds = new AbstractList<ProtoId>() {
87         @Override public ProtoId get(int index) {
88             checkBounds(index, tableOfContents.protoIds.size);
89             return open(tableOfContents.protoIds.off + (SizeOf.PROTO_ID_ITEM * index))
90                     .readProtoId();
91         }
92         @Override public int size() {
93             return tableOfContents.protoIds.size;
94         }
95     };
96 
97     private final List<FieldId> fieldIds = new AbstractList<FieldId>() {
98         @Override public FieldId get(int index) {
99             checkBounds(index, tableOfContents.fieldIds.size);
100             return open(tableOfContents.fieldIds.off + (SizeOf.MEMBER_ID_ITEM * index))
101                     .readFieldId();
102         }
103         @Override public int size() {
104             return tableOfContents.fieldIds.size;
105         }
106     };
107 
108     private final List<MethodId> methodIds = new AbstractList<MethodId>() {
109         @Override public MethodId get(int index) {
110             checkBounds(index, tableOfContents.methodIds.size);
111             return open(tableOfContents.methodIds.off + (SizeOf.MEMBER_ID_ITEM * index))
112                     .readMethodId();
113         }
114         @Override public int size() {
115             return tableOfContents.methodIds.size;
116         }
117     };
118 
119     /**
120      * Creates a new dex buffer defining no classes.
121      */
DexBuffer()122     public DexBuffer() {
123         this.data = new byte[0];
124     }
125 
126     /**
127      * Creates a new dex buffer that reads from {@code data}. It is an error to
128      * modify {@code data} after using it to create a dex buffer.
129      */
DexBuffer(byte[] data)130     public DexBuffer(byte[] data) throws IOException {
131         this.data = data;
132         this.length = data.length;
133         this.tableOfContents.readFrom(this);
134     }
135 
136     /**
137      * Creates a new dex buffer of the dex in {@code in}, and closes {@code in}.
138      */
DexBuffer(InputStream in)139     public DexBuffer(InputStream in) throws IOException {
140         loadFrom(in);
141     }
142 
143     /**
144      * Creates a new dex buffer from the dex file {@code file}.
145      */
DexBuffer(File file)146     public DexBuffer(File file) throws IOException {
147         if (FileUtils.hasArchiveSuffix(file.getName())) {
148             ZipFile zipFile = new ZipFile(file);
149             ZipEntry entry = zipFile.getEntry(DexFormat.DEX_IN_JAR_NAME);
150             if (entry != null) {
151                 loadFrom(zipFile.getInputStream(entry));
152                 zipFile.close();
153             } else {
154                 throw new DexException("Expected " + DexFormat.DEX_IN_JAR_NAME + " in " + file);
155             }
156         } else if (file.getName().endsWith(".dex")) {
157             loadFrom(new FileInputStream(file));
158         } else {
159             throw new DexException("unknown output extension: " + file);
160         }
161     }
162 
loadFrom(InputStream in)163     private void loadFrom(InputStream in) throws IOException {
164         ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
165         byte[] buffer = new byte[8192];
166 
167         int count;
168         while ((count = in.read(buffer)) != -1) {
169             bytesOut.write(buffer, 0, count);
170         }
171         in.close();
172 
173         this.data = bytesOut.toByteArray();
174         this.length = data.length;
175         this.tableOfContents.readFrom(this);
176     }
177 
checkBounds(int index, int length)178     private static void checkBounds(int index, int length) {
179         if (index < 0 || index >= length) {
180             throw new IndexOutOfBoundsException("index:" + index + ", length=" + length);
181         }
182     }
183 
writeTo(OutputStream out)184     public void writeTo(OutputStream out) throws IOException {
185         out.write(data);
186     }
187 
writeTo(File dexOut)188     public void writeTo(File dexOut) throws IOException {
189         OutputStream out = new FileOutputStream(dexOut);
190         writeTo(out);
191         out.close();
192     }
193 
getTableOfContents()194     public TableOfContents getTableOfContents() {
195         return tableOfContents;
196     }
197 
open(int position)198     public Section open(int position) {
199         if (position < 0 || position > length) {
200             throw new IllegalArgumentException("position=" + position + " length=" + length);
201         }
202         return new Section(position);
203     }
204 
appendSection(int maxByteCount, String name)205     public Section appendSection(int maxByteCount, String name) {
206         int limit = fourByteAlign(length + maxByteCount);
207         Section result = new Section(name, length, limit);
208         length = limit;
209         return result;
210     }
211 
noMoreSections()212     public void noMoreSections() {
213         data = new byte[length];
214     }
215 
getLength()216     public int getLength() {
217         return length;
218     }
219 
fourByteAlign(int position)220     public static int fourByteAlign(int position) {
221         return (position + 3) & ~3;
222     }
223 
getBytes()224     public byte[] getBytes() {
225         return data;
226     }
227 
strings()228     public List<String> strings() {
229         return strings;
230     }
231 
typeIds()232     public List<Integer> typeIds() {
233         return typeIds;
234     }
235 
typeNames()236     public List<String> typeNames() {
237         return typeNames;
238     }
239 
protoIds()240     public List<ProtoId> protoIds() {
241         return protoIds;
242     }
243 
fieldIds()244     public List<FieldId> fieldIds() {
245         return fieldIds;
246     }
247 
methodIds()248     public List<MethodId> methodIds() {
249         return methodIds;
250     }
251 
classDefs()252     public Iterable<ClassDef> classDefs() {
253         return new Iterable<ClassDef>() {
254             public Iterator<ClassDef> iterator() {
255                 if (!tableOfContents.classDefs.exists()) {
256                     return Collections.<ClassDef>emptySet().iterator();
257                 }
258                 return new Iterator<ClassDef>() {
259                     private DexBuffer.Section in = open(tableOfContents.classDefs.off);
260                     private int count = 0;
261 
262                     public boolean hasNext() {
263                         return count < tableOfContents.classDefs.size;
264                     }
265                     public ClassDef next() {
266                         if (!hasNext()) {
267                             throw new NoSuchElementException();
268                         }
269                         count++;
270                         return in.readClassDef();
271                     }
272                     public void remove() {
273                         throw new UnsupportedOperationException();
274                     }
275                 };
276             }
277         };
278     }
279 
280     public TypeList readTypeList(int offset) {
281         if (offset == 0) {
282             return TypeList.EMPTY;
283         }
284         return open(offset).readTypeList();
285     }
286 
287     public ClassData readClassData(ClassDef classDef) {
288         int offset = classDef.getClassDataOffset();
289         if (offset == 0) {
290             throw new IllegalArgumentException("offset == 0");
291         }
292         return open(offset).readClassData();
293     }
294 
295     public Code readCode(ClassData.Method method) {
296         int offset = method.getCodeOffset();
297         if (offset == 0) {
298             throw new IllegalArgumentException("offset == 0");
299         }
300         return open(offset).readCode();
301     }
302 
303     public final class Section implements ByteInput, ByteOutput {
304         private final String name;
305         private int position;
306         private final int limit;
307         private final int initialPosition;
308 
309         private Section(String name, int position, int limit) {
310             this.name = name;
311             this.position = this.initialPosition = position;
312             this.limit = limit;
313         }
314 
315         private Section(int position) {
316             this("section", position, data.length);
317         }
318 
319         public int getPosition() {
320             return position;
321         }
322 
323         public int readInt() {
324             int result = (data[position] & 0xff)
325                     | (data[position + 1] & 0xff) << 8
326                     | (data[position + 2] & 0xff) << 16
327                     | (data[position + 3] & 0xff) << 24;
328             position += 4;
329             return result;
330         }
331 
332         public short readShort() {
333             int result = (data[position] & 0xff)
334                     | (data[position + 1] & 0xff) << 8;
335             position += 2;
336             return (short) result;
337         }
338 
339         public int readUnsignedShort() {
340             return readShort() & 0xffff;
341         }
342 
343         public byte readByte() {
344             return (byte) (data[position++] & 0xff);
345         }
346 
347         public byte[] readByteArray(int length) {
348             byte[] result = Arrays.copyOfRange(data, position, position + length);
349             position += length;
350             return result;
351         }
352 
353         public short[] readShortArray(int length) {
354             short[] result = new short[length];
355             for (int i = 0; i < length; i++) {
356                 result[i] = readShort();
357             }
358             return result;
359         }
360 
361         public int readUleb128() {
362             return Leb128Utils.readUnsignedLeb128(this);
363         }
364 
365         public int readSleb128() {
366             return Leb128Utils.readSignedLeb128(this);
367         }
368 
369         public TypeList readTypeList() {
370             int size = readInt();
371             short[] types = new short[size];
372             for (int i = 0; i < size; i++) {
373                 types[i] = readShort();
374             }
375             alignToFourBytes();
376             return new TypeList(DexBuffer.this, types);
377         }
378 
379         public String readString() {
380             int offset = readInt();
381             int savedPosition = position;
382             position = offset;
383             try {
384                 int expectedLength = readUleb128();
385                 String result = Mutf8.decode(this, new char[expectedLength]);
386                 if (result.length() != expectedLength) {
387                     throw new DexException("Declared length " + expectedLength
388                             + " doesn't match decoded length of " + result.length());
389                 }
390                 return result;
391             } catch (UTFDataFormatException e) {
392                 throw new DexException(e);
393             } finally {
394                 position = savedPosition;
395             }
396         }
397 
398         public FieldId readFieldId() {
399             int declaringClassIndex = readUnsignedShort();
400             int typeIndex = readUnsignedShort();
401             int nameIndex = readInt();
402             return new FieldId(DexBuffer.this, declaringClassIndex, typeIndex, nameIndex);
403         }
404 
405         public MethodId readMethodId() {
406             int declaringClassIndex = readUnsignedShort();
407             int protoIndex = readUnsignedShort();
408             int nameIndex = readInt();
409             return new MethodId(DexBuffer.this, declaringClassIndex, protoIndex, nameIndex);
410         }
411 
412         public ProtoId readProtoId() {
413             int shortyIndex = readInt();
414             int returnTypeIndex = readInt();
415             int parametersOffset = readInt();
416             return new ProtoId(DexBuffer.this, shortyIndex, returnTypeIndex, parametersOffset);
417         }
418 
419         public ClassDef readClassDef() {
420             int offset = getPosition();
421             int type = readInt();
422             int accessFlags = readInt();
423             int supertype = readInt();
424             int interfacesOffset = readInt();
425             int sourceFileIndex = readInt();
426             int annotationsOffset = readInt();
427             int classDataOffset = readInt();
428             int staticValuesOffset = readInt();
429             return new ClassDef(DexBuffer.this, offset, type, accessFlags, supertype,
430                     interfacesOffset, sourceFileIndex, annotationsOffset, classDataOffset,
431                     staticValuesOffset);
432         }
433 
434         private Code readCode() {
435             int registersSize = readUnsignedShort();
436             int insSize = readUnsignedShort();
437             int outsSize = readUnsignedShort();
438             int triesSize = readUnsignedShort();
439             int debugInfoOffset = readInt();
440             int instructionsSize = readInt();
441             short[] instructions = readShortArray(instructionsSize);
442             Code.Try[] tries = new Code.Try[triesSize];
443             Code.CatchHandler[] catchHandlers = new Code.CatchHandler[0];
444             if (triesSize > 0) {
445                 if (instructions.length % 2 == 1) {
446                     readShort(); // padding
447                 }
448 
449                 for (int i = 0; i < triesSize; i++) {
450                     int startAddress = readInt();
451                     int instructionCount = readUnsignedShort();
452                     int handlerOffset = readUnsignedShort();
453                     tries[i] = new Code.Try(startAddress, instructionCount, handlerOffset);
454                 }
455 
456                 int catchHandlersSize = readUleb128();
457                 catchHandlers = new Code.CatchHandler[catchHandlersSize];
458                 for (int i = 0; i < catchHandlersSize; i++) {
459                     catchHandlers[i] = readCatchHandler();
460                 }
461             }
462             return new Code(registersSize, insSize, outsSize, debugInfoOffset, instructions,
463                     tries, catchHandlers);
464         }
465 
466         private Code.CatchHandler readCatchHandler() {
467             int size = readSleb128();
468             int handlersCount = Math.abs(size);
469             int[] typeIndexes = new int[handlersCount];
470             int[] addresses = new int[handlersCount];
471             for (int i = 0; i < handlersCount; i++) {
472                 typeIndexes[i] = readUleb128();
473                 addresses[i] = readUleb128();
474             }
475             int catchAllAddress = size <= 0 ? readUleb128() : -1;
476             return new Code.CatchHandler(typeIndexes, addresses, catchAllAddress);
477         }
478 
479         private ClassData readClassData() {
480             int staticFieldsSize = readUleb128();
481             int instanceFieldsSize = readUleb128();
482             int directMethodsSize = readUleb128();
483             int virtualMethodsSize = readUleb128();
484             ClassData.Field[] staticFields = readFields(staticFieldsSize);
485             ClassData.Field[] instanceFields = readFields(instanceFieldsSize);
486             ClassData.Method[] directMethods = readMethods(directMethodsSize);
487             ClassData.Method[] virtualMethods = readMethods(virtualMethodsSize);
488             return new ClassData(staticFields, instanceFields, directMethods, virtualMethods);
489         }
490 
491         private ClassData.Field[] readFields(int count) {
492             ClassData.Field[] result = new ClassData.Field[count];
493             int fieldIndex = 0;
494             for (int i = 0; i < count; i++) {
495                 fieldIndex += readUleb128(); // field index diff
496                 int accessFlags = readUleb128();
497                 result[i] = new ClassData.Field(fieldIndex, accessFlags);
498             }
499             return result;
500         }
501 
502         private ClassData.Method[] readMethods(int count) {
503             ClassData.Method[] result = new ClassData.Method[count];
504             int methodIndex = 0;
505             for (int i = 0; i < count; i++) {
506                 methodIndex += readUleb128(); // method index diff
507                 int accessFlags = readUleb128();
508                 int codeOff = readUleb128();
509                 result[i] = new ClassData.Method(methodIndex, accessFlags, codeOff);
510             }
511             return result;
512         }
513 
514         public Annotation readAnnotation() {
515             byte visibility = readByte();
516             int typeIndex = readUleb128();
517             int size = readUleb128();
518             int[] names = new int[size];
519             EncodedValue[] values = new EncodedValue[size];
520             for (int i = 0; i < size; i++) {
521                 names[i] = readUleb128();
522                 values[i] = readEncodedValue();
523             }
524             return new Annotation(DexBuffer.this, visibility, typeIndex, names, values);
525         }
526 
527         public EncodedValue readEncodedValue() {
528             int start = position;
529             new EncodedValueReader(this).readValue();
530             int end = position;
531             return new EncodedValue(Arrays.copyOfRange(data, start, end));
532         }
533 
534         public EncodedValue readEncodedArray() {
535             int start = position;
536             new EncodedValueReader(this).readArray();
537             int end = position;
538             return new EncodedValue(Arrays.copyOfRange(data, start, end));
539         }
540 
541         private void ensureCapacity(int size) {
542             if (position + size > limit) {
543                 throw new DexException("Section limit " + limit + " exceeded by " + name);
544             }
545         }
546 
547         /**
548          * Writes 0x00 until the position is aligned to a multiple of 4.
549          */
550         public void alignToFourBytes() {
551             int unalignedCount = position;
552             position = DexBuffer.fourByteAlign(position);
553             for (int i = unalignedCount; i < position; i++) {
554                 data[i] = 0;
555             }
556         }
557 
558         public void assertFourByteAligned() {
559             if ((position & 3) != 0) {
560                 throw new IllegalStateException("Not four byte aligned!");
561             }
562         }
563 
564         public void write(byte[] bytes) {
565             ensureCapacity(bytes.length);
566             System.arraycopy(bytes, 0, data, position, bytes.length);
567             position += bytes.length;
568         }
569 
570         public void writeByte(int b) {
571             ensureCapacity(1);
572             data[position++] = (byte) b;
573         }
574 
575         public void writeShort(short i) {
576             ensureCapacity(2);
577             data[position    ] = (byte) i;
578             data[position + 1] = (byte) (i >>> 8);
579             position += 2;
580         }
581 
582         public void writeUnsignedShort(int i) {
583             short s = (short) i;
584             if (i != (s & 0xffff)) {
585                 throw new IllegalArgumentException("Expected an unsigned short: " + i);
586             }
587             writeShort(s);
588         }
589 
590         public void write(short[] shorts) {
591             for (short s : shorts) {
592                 writeShort(s);
593             }
594         }
595 
596         public void writeInt(int i) {
597             ensureCapacity(4);
598             data[position    ] = (byte) i;
599             data[position + 1] = (byte) (i >>>  8);
600             data[position + 2] = (byte) (i >>> 16);
601             data[position + 3] = (byte) (i >>> 24);
602             position += 4;
603         }
604 
605         public void writeUleb128(int i) {
606             try {
607                 Leb128Utils.writeUnsignedLeb128(this, i);
608                 ensureCapacity(0);
609             } catch (ArrayIndexOutOfBoundsException e) {
610                 throw new DexException("Section limit " + limit + " exceeded by " + name);
611             }
612         }
613 
614         public void writeSleb128(int i) {
615             try {
616                 Leb128Utils.writeSignedLeb128(this, i);
617                 ensureCapacity(0);
618             } catch (ArrayIndexOutOfBoundsException e) {
619                 throw new DexException("Section limit " + limit + " exceeded by " + name);
620             }
621         }
622 
623         public void writeStringData(String value) {
624             try {
625                 int length = value.length();
626                 writeUleb128(length);
627                 write(Mutf8.encode(value));
628                 writeByte(0);
629             } catch (UTFDataFormatException e) {
630                 throw new AssertionError();
631             }
632         }
633 
634         public void writeTypeList(TypeList typeList) {
635             short[] types = typeList.getTypes();
636             writeInt(types.length);
637             for (short type : types) {
638                 writeShort(type);
639             }
640             alignToFourBytes();
641         }
642 
643         /**
644          * Returns the number of bytes remaining in this section.
645          */
646         public int remaining() {
647             return limit - position;
648         }
649 
650         /**
651          * Returns the number of bytes used by this section.
652          */
653         public int used () {
654             return position - initialPosition;
655         }
656     }
657 }
658