• 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     private 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 
308         private Section(String name, int position, int limit) {
309             this.name = name;
310             this.position = position;
311             this.limit = limit;
312         }
313 
314         private Section(int position) {
315             this("section", position, data.length);
316         }
317 
318         public int getPosition() {
319             return position;
320         }
321 
322         public int readInt() {
323             int result = (data[position] & 0xff)
324                     | (data[position + 1] & 0xff) << 8
325                     | (data[position + 2] & 0xff) << 16
326                     | (data[position + 3] & 0xff) << 24;
327             position += 4;
328             return result;
329         }
330 
331         public short readShort() {
332             int result = (data[position] & 0xff)
333                     | (data[position + 1] & 0xff) << 8;
334             position += 2;
335             return (short) result;
336         }
337 
338         public int readUnsignedShort() {
339             return readShort() & 0xffff;
340         }
341 
342         public byte readByte() {
343             return (byte) (data[position++] & 0xff);
344         }
345 
346         public byte[] readByteArray(int length) {
347             byte[] result = Arrays.copyOfRange(data, position, position + length);
348             position += length;
349             return result;
350         }
351 
352         public short[] readShortArray(int length) {
353             short[] result = new short[length];
354             for (int i = 0; i < length; i++) {
355                 result[i] = readShort();
356             }
357             return result;
358         }
359 
360         public int readUleb128() {
361             return Leb128Utils.readUnsignedLeb128(this);
362         }
363 
364         public int readSleb128() {
365             return Leb128Utils.readSignedLeb128(this);
366         }
367 
368         public TypeList readTypeList() {
369             int size = readInt();
370             short[] types = new short[size];
371             for (int i = 0; i < size; i++) {
372                 types[i] = readShort();
373             }
374             alignToFourBytes();
375             return new TypeList(DexBuffer.this, types);
376         }
377 
378         public String readString() {
379             int offset = readInt();
380             int savedPosition = position;
381             position = offset;
382             try {
383                 int expectedLength = readUleb128();
384                 String result = Mutf8.decode(this, new char[expectedLength]);
385                 if (result.length() != expectedLength) {
386                     throw new DexException("Declared length " + expectedLength
387                             + " doesn't match decoded length of " + result.length());
388                 }
389                 return result;
390             } catch (UTFDataFormatException e) {
391                 throw new DexException(e);
392             } finally {
393                 position = savedPosition;
394             }
395         }
396 
397         public FieldId readFieldId() {
398             int declaringClassIndex = readUnsignedShort();
399             int typeIndex = readUnsignedShort();
400             int nameIndex = readInt();
401             return new FieldId(DexBuffer.this, declaringClassIndex, typeIndex, nameIndex);
402         }
403 
404         public MethodId readMethodId() {
405             int declaringClassIndex = readUnsignedShort();
406             int protoIndex = readUnsignedShort();
407             int nameIndex = readInt();
408             return new MethodId(DexBuffer.this, declaringClassIndex, protoIndex, nameIndex);
409         }
410 
411         public ProtoId readProtoId() {
412             int shortyIndex = readInt();
413             int returnTypeIndex = readInt();
414             int parametersOffset = readInt();
415             return new ProtoId(DexBuffer.this, shortyIndex, returnTypeIndex, parametersOffset);
416         }
417 
418         public ClassDef readClassDef() {
419             int offset = getPosition();
420             int type = readInt();
421             int accessFlags = readInt();
422             int supertype = readInt();
423             int interfacesOffset = readInt();
424             int sourceFileIndex = readInt();
425             int annotationsOffset = readInt();
426             int classDataOffset = readInt();
427             int staticValuesOffset = readInt();
428             return new ClassDef(DexBuffer.this, offset, type, accessFlags, supertype,
429                     interfacesOffset, sourceFileIndex, annotationsOffset, classDataOffset,
430                     staticValuesOffset);
431         }
432 
433         private Code readCode() {
434             int registersSize = readUnsignedShort();
435             int insSize = readUnsignedShort();
436             int outsSize = readUnsignedShort();
437             int triesSize = readUnsignedShort();
438             int debugInfoOffset = readInt();
439             int instructionsSize = readInt();
440             short[] instructions = readShortArray(instructionsSize);
441             Code.Try[] tries = new Code.Try[triesSize];
442             Code.CatchHandler[] catchHandlers = new Code.CatchHandler[0];
443             if (triesSize > 0) {
444                 if (instructions.length % 2 == 1) {
445                     readShort(); // padding
446                 }
447 
448                 for (int i = 0; i < triesSize; i++) {
449                     int startAddress = readInt();
450                     int instructionCount = readUnsignedShort();
451                     int handlerOffset = readUnsignedShort();
452                     tries[i] = new Code.Try(startAddress, instructionCount, handlerOffset);
453                 }
454 
455                 int catchHandlersSize = readUleb128();
456                 catchHandlers = new Code.CatchHandler[catchHandlersSize];
457                 for (int i = 0; i < catchHandlersSize; i++) {
458                     catchHandlers[i] = readCatchHandler();
459                 }
460             }
461             return new Code(registersSize, insSize, outsSize, debugInfoOffset, instructions,
462                     tries, catchHandlers);
463         }
464 
465         private Code.CatchHandler readCatchHandler() {
466             int size = readSleb128();
467             int handlersCount = Math.abs(size);
468             int[] typeIndexes = new int[handlersCount];
469             int[] addresses = new int[handlersCount];
470             for (int i = 0; i < handlersCount; i++) {
471                 typeIndexes[i] = readUleb128();
472                 addresses[i] = readUleb128();
473             }
474             int catchAllAddress = size <= 0 ? readUleb128() : -1;
475             return new Code.CatchHandler(typeIndexes, addresses, catchAllAddress);
476         }
477 
478         private ClassData readClassData() {
479             int staticFieldsSize = readUleb128();
480             int instanceFieldsSize = readUleb128();
481             int directMethodsSize = readUleb128();
482             int virtualMethodsSize = readUleb128();
483             ClassData.Field[] staticFields = readFields(staticFieldsSize);
484             ClassData.Field[] instanceFields = readFields(instanceFieldsSize);
485             ClassData.Method[] directMethods = readMethods(directMethodsSize);
486             ClassData.Method[] virtualMethods = readMethods(virtualMethodsSize);
487             return new ClassData(staticFields, instanceFields, directMethods, virtualMethods);
488         }
489 
490         private ClassData.Field[] readFields(int count) {
491             ClassData.Field[] result = new ClassData.Field[count];
492             int fieldIndex = 0;
493             for (int i = 0; i < count; i++) {
494                 fieldIndex += readUleb128(); // field index diff
495                 int accessFlags = readUleb128();
496                 result[i] = new ClassData.Field(fieldIndex, accessFlags);
497             }
498             return result;
499         }
500 
501         private ClassData.Method[] readMethods(int count) {
502             ClassData.Method[] result = new ClassData.Method[count];
503             int methodIndex = 0;
504             for (int i = 0; i < count; i++) {
505                 methodIndex += readUleb128(); // method index diff
506                 int accessFlags = readUleb128();
507                 int codeOff = readUleb128();
508                 result[i] = new ClassData.Method(methodIndex, accessFlags, codeOff);
509             }
510             return result;
511         }
512 
513         public Annotation readAnnotation() {
514             byte visibility = readByte();
515             int typeIndex = readUleb128();
516             int size = readUleb128();
517             int[] names = new int[size];
518             EncodedValue[] values = new EncodedValue[size];
519             for (int i = 0; i < size; i++) {
520                 names[i] = readUleb128();
521                 values[i] = readEncodedValue();
522             }
523             return new Annotation(DexBuffer.this, visibility, typeIndex, names, values);
524         }
525 
526         public EncodedValue readEncodedValue() {
527             int start = position;
528             new EncodedValueReader(this).readValue();
529             int end = position;
530             return new EncodedValue(Arrays.copyOfRange(data, start, end));
531         }
532 
533         public EncodedValue readEncodedArray() {
534             int start = position;
535             new EncodedValueReader(this).readArray();
536             int end = position;
537             return new EncodedValue(Arrays.copyOfRange(data, start, end));
538         }
539 
540         private void ensureCapacity(int size) {
541             if (position + size > limit) {
542                 throw new DexException("Section limit " + limit + " exceeded by " + name);
543             }
544         }
545 
546         /**
547          * Writes 0x00 until the position is aligned to a multiple of 4.
548          */
549         public void alignToFourBytes() {
550             int unalignedCount = position;
551             position = DexBuffer.fourByteAlign(position);
552             for (int i = unalignedCount; i < position; i++) {
553                 data[i] = 0;
554             }
555         }
556 
557         public void assertFourByteAligned() {
558             if ((position & 3) != 0) {
559                 throw new IllegalStateException("Not four byte aligned!");
560             }
561         }
562 
563         public void write(byte[] bytes) {
564             ensureCapacity(bytes.length);
565             System.arraycopy(bytes, 0, data, position, bytes.length);
566             position += bytes.length;
567         }
568 
569         public void writeByte(int b) {
570             ensureCapacity(1);
571             data[position++] = (byte) b;
572         }
573 
574         public void writeShort(short i) {
575             ensureCapacity(2);
576             data[position    ] = (byte) i;
577             data[position + 1] = (byte) (i >>> 8);
578             position += 2;
579         }
580 
581         public void writeUnsignedShort(int i) {
582             short s = (short) i;
583             if (i != (s & 0xffff)) {
584                 throw new IllegalArgumentException("Expected an unsigned short: " + i);
585             }
586             writeShort(s);
587         }
588 
589         public void write(short[] shorts) {
590             for (short s : shorts) {
591                 writeShort(s);
592             }
593         }
594 
595         public void writeInt(int i) {
596             ensureCapacity(4);
597             data[position    ] = (byte) i;
598             data[position + 1] = (byte) (i >>>  8);
599             data[position + 2] = (byte) (i >>> 16);
600             data[position + 3] = (byte) (i >>> 24);
601             position += 4;
602         }
603 
604         public void writeUleb128(int i) {
605             try {
606                 Leb128Utils.writeUnsignedLeb128(this, i);
607                 ensureCapacity(0);
608             } catch (ArrayIndexOutOfBoundsException e) {
609                 throw new DexException("Section limit " + limit + " exceeded by " + name);
610             }
611         }
612 
613         public void writeSleb128(int i) {
614             try {
615                 Leb128Utils.writeSignedLeb128(this, i);
616                 ensureCapacity(0);
617             } catch (ArrayIndexOutOfBoundsException e) {
618                 throw new DexException("Section limit " + limit + " exceeded by " + name);
619             }
620         }
621 
622         public void writeStringData(String value) {
623             try {
624                 int length = value.length();
625                 writeUleb128(length);
626                 write(Mutf8.encode(value));
627                 writeByte(0);
628             } catch (UTFDataFormatException e) {
629                 throw new AssertionError();
630             }
631         }
632 
633         public void writeTypeList(TypeList typeList) {
634             short[] types = typeList.getTypes();
635             writeInt(types.length);
636             for (short type : types) {
637                 writeShort(type);
638             }
639             alignToFourBytes();
640         }
641 
642         /**
643          * Returns the number of bytes remaining in this section.
644          */
645         public int remaining() {
646             return limit - position;
647         }
648     }
649 }
650