DexFile.java revision fdef6422d2c5c87c0a6599bd568943d493436820
1/*
2 * [The "BSD licence"]
3 * Copyright (c) 2009 Ben Gruver
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 *    derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29package org.jf.dexlib;
30
31import org.jf.dexlib.Util.AnnotatedOutput;
32import org.jf.dexlib.Util.ByteArrayInput;
33import org.jf.dexlib.Util.FileUtils;
34import org.jf.dexlib.Util.Input;
35
36import java.io.File;
37import java.security.DigestException;
38import java.security.MessageDigest;
39import java.security.NoSuchAlgorithmException;
40import java.util.HashMap;
41import java.util.zip.Adler32;
42
43    /**
44     * <h3>Main use cases</h3>
45     *
46     * <p>These are the main use cases that drove the design of this library</p>
47     *
48     * <ol>
49     * <li><p><b>Annotate an existing dex file</b> - In this case, the intent is to document the structure of
50     *    an existing dex file. We want to be able to read in the dex file, and then write out a dex file
51     *    that is exactly the same (while adding annotation information to an AnnotatedOutput object)</p></li>
52     *
53     * <li><p><b>Canonicalize an existing dex file</b> - In this case, the intent is to rewrite an existing dex file
54     *    so that it is in a canonical form. There is a certain amount of leeway in how various types of
55     *    tems in a dex file are ordered or represented. It is sometimes useful to be able to easily
56     *    compare a disassebled and reassembled dex file with the original dex file. If both dex-files are
57     *    written canonically, they "should" match exactly, barring any explicit changes to the reassembled
58     *    file.</p>
59     *
60     *    <p>Currently, there are a couple of pieces of information that probably won't match exactly
61     *    <ul>
62     *    <li>the order of exception handlers in the <code>EncodedCatchHandlerList</code> for a method</li>
63     *    <li>the ordering of some of the debug info in the <code>{@link DebugInfoItem}</code> for a method</li>
64     *    </ul></p>
65     *
66     *
67     *    <p>Note that the above discrepancies should typically only be "intra-item" differences. They
68     *    shouldn't change the size of the item, or affect how anything else is placed or laid out</p></li>
69     *
70     * <li><p><b>Creating a dex file from scratch</b> - In this case, a blank dex file is created and then classes
71     *    are added to it incrementally by calling the {@link Section#intern intern} method of
72     *    {@link DexFile#ClassDefsSection}, which will add all the information necessary to represent the given
73     *    class. For example, when assembling a dex file from a set of assembly text files.</p>
74     *
75     *    <p>In this case, we can choose to write  the dex file in a canonical form or not. It is somewhat
76     *    slower to write it in a canonical format, due to the extra sorting and calculations that are
77     *    required.</p></li>
78     *
79     *
80     * <li><p><b>Reading in the dex file</b> - In this case, the intent is to read in a dex file and expose all the
81     *    data to the calling application. For example, when disassembling a dex file into a text based
82     *    assembly format, or doing other misc processing of the dex file.</p></li>
83     *
84     *
85     * <h3>Other use cases</h3>
86     *
87     * <p>These are other use cases that are possible, but did not drive the design of the library.
88     * No effort was made to test these use cases or ensure that they work. Some of these could
89     * probably be better achieved with a disassemble - modify - reassemble type process, using
90     * smali/baksmali or another assembler/disassembler pair that are compatible with each other</p>
91     *
92     * <ul>
93     * <li>deleting classes/methods/etc. from a dex file</li>
94     * <li>merging 2 dex files</li>
95     * <li>splitting a dex file</li>
96     * <li>moving classes from 1 dex file to another</li>
97     * <li>removing the debug information from a dex file</li>
98     * <li>obfustication of a dex file</li>
99     * </ul>
100     */
101public class DexFile
102{
103    /**
104     * A mapping from ItemType to the section that contains items of the given type
105     */
106    private final HashMap<ItemType, Section> sectionsByType;
107
108    /**
109     * Ordered lists of the indexed and offsetted sections. The order of these lists specifies the order
110     * that the sections will be written in
111     */
112    private final IndexedSection[] indexedSections;
113    private final OffsettedSection[] offsettedSections;
114
115    /**
116     * dalvik had a bug where it wrote the registers for certain types of debug info in a signed leb
117     * format, instead of an unsigned leb format. There are no negative registers of course, but
118     * certain positive values have a different encoding depending on whether they are encoded as
119     * an unsigned leb128 or a signed leb128. Specifically, the signed leb128 is 1 byte longer in some cases.
120     *
121     * This determine whether we should keep any signed registers as signed, or force all register to
122     * unsigned. By default we don't keep track of whether they were signed or not, and write them back
123     * out as unsigned. This option only has an effect when reading an existing dex file. It has no
124     * effect when a dex file is created from scratch
125     *
126     * The 2 main use-cases in play are
127     * 1. Annotate an existing dex file - In this case, preserveSignedRegisters should be false, so that we keep
128     * track of any signed registers and write them back out as signed Leb128 values.
129     *
130     * 2. Canonicalize an existing dex file - In this case, fixRegisters should be true, so that all
131     * registers in the debug info are written as unsigned Leb128 values regardless of how they were
132     * originally encoded
133     */
134    private final boolean preserveSignedRegisters;
135
136    /**
137     * When true, this prevents any sorting of the items during placement of the dex file. This
138     * should *only* be set to true when this dex file was read in from an existing (valid) dex file,
139     * and no modifications were made (i.e. no items added or deleted). Otherwise it is likely that
140     * an invalid dex file will be generated.
141     *
142     * This is useful for the first use case (annotating an existing dex file). This ensures the items
143     * retain the same order as in the original dex file.
144     */
145    private boolean inplace = false;
146
147    /**
148     * When true, this imposes an full ordering on all the items, to force them into a (possibly
149     * arbitrary) canonical order. When false, only the items that the dex format specifies
150     * an order for are sorted. The rest of the items are not ordered.
151     *
152     * This is useful for the second use case (canonicalizing an existing dex file) or possibly for
153     * the third use case (creating a dex file from scratch), if there is a need to write the new
154     * dex file in a canonical form.
155     */
156    private boolean sortAllItems = false;
157
158
159    /**
160     * this is used to access the dex file from within inner classes, when they declare fields or
161     * variable that hide fields on this object
162     */
163    private final DexFile dexFile = this;
164
165
166    /**
167     * A private constructor containing common code to initialize the section maps and lists
168     * @param preserveSignedRegisters If true, keep track of any registers in the debug information
169     * that are signed, so they will be written in the same format. See
170     * <code>getPreserveSignedRegisters()</code>
171     */
172    private DexFile(boolean preserveSignedRegisters) {
173        this.preserveSignedRegisters = preserveSignedRegisters;
174
175        sectionsByType = new HashMap<ItemType, Section>(18);
176
177        sectionsByType.put(ItemType.TYPE_ANNOTATION_ITEM, AnnotationsSection);
178        sectionsByType.put(ItemType.TYPE_ANNOTATION_SET_ITEM, AnnotationSetsSection);
179        sectionsByType.put(ItemType.TYPE_ANNOTATION_SET_REF_LIST, AnnotationSetRefListsSection);
180        sectionsByType.put(ItemType.TYPE_ANNOTATIONS_DIRECTORY_ITEM, AnnotationDirectoriesSection);
181        sectionsByType.put(ItemType.TYPE_CLASS_DATA_ITEM, ClassDataSection);
182        sectionsByType.put(ItemType.TYPE_CLASS_DEF_ITEM, ClassDefsSection);
183        sectionsByType.put(ItemType.TYPE_CODE_ITEM, CodeItemsSection);
184        sectionsByType.put(ItemType.TYPE_DEBUG_INFO_ITEM, DebugInfoItemsSection);
185        sectionsByType.put(ItemType.TYPE_ENCODED_ARRAY_ITEM, EncodedArraysSection);
186        sectionsByType.put(ItemType.TYPE_FIELD_ID_ITEM, FieldIdsSection);
187        sectionsByType.put(ItemType.TYPE_HEADER_ITEM, HeaderItemSection);
188        sectionsByType.put(ItemType.TYPE_MAP_LIST, MapSection);
189        sectionsByType.put(ItemType.TYPE_METHOD_ID_ITEM, MethodIdsSection);
190        sectionsByType.put(ItemType.TYPE_PROTO_ID_ITEM, ProtoIdsSection);
191        sectionsByType.put(ItemType.TYPE_STRING_DATA_ITEM, StringDataSection);
192        sectionsByType.put(ItemType.TYPE_STRING_ID_ITEM, StringIdsSection);
193        sectionsByType.put(ItemType.TYPE_TYPE_ID_ITEM, TypeIdsSection);
194        sectionsByType.put(ItemType.TYPE_TYPE_LIST, TypeListsSection);
195
196        indexedSections = new IndexedSection[] {
197                StringIdsSection,
198                TypeIdsSection,
199                ProtoIdsSection,
200                FieldIdsSection,
201                MethodIdsSection,
202                ClassDefsSection
203        };
204
205        offsettedSections = new OffsettedSection[] {
206                AnnotationSetRefListsSection,
207                AnnotationSetsSection,
208                CodeItemsSection,
209                AnnotationDirectoriesSection,
210                TypeListsSection,
211                StringDataSection,
212                AnnotationsSection,
213                EncodedArraysSection,
214                ClassDataSection,
215                DebugInfoItemsSection
216        };
217    }
218
219
220    /**
221     * Construct a new DexFile instance by reading in the given dex file.
222     * @param file The dex file to read in
223     */
224    public DexFile(String file) {
225        this(new File(file), true);
226    }
227
228    /**
229     * Construct a new DexFile instance by reading in the given dex file,
230     * and optionally keep track of any registers in the debug information that are signed,
231     * so they will be written in the same format.
232     * @param file The dex file to read in
233     * @param preserveSignedRegisters If true, keep track of any registers in the debug information
234     * that are signed, so they will be written in the same format. See
235     * <code>getPreserveSignedRegisters()</code>
236     */
237    public DexFile(String file, boolean preserveSignedRegisters) {
238        this(new File(file), preserveSignedRegisters);
239    }
240
241    /**
242     * Construct a new DexFile instead by reading in the given dex file.
243     * @param file The dex file to read in
244     */
245    public DexFile(File file) {
246        this(file, true);
247    }
248
249    /**
250     * Construct a new DexFile instance by reading in the given dex file,
251     * and optionally keep track of any registers in the debug information that are signed,
252     * so they will be written in the same format.
253     * @param file The dex file to read in
254     * @param preserveSignedRegisters If true, keep track of any registers in the debug information
255     * that are signed, so they will be written in the same format.
256     * @see #getPreserveSignedRegisters
257     */
258    public DexFile(File file, boolean preserveSignedRegisters) {
259        this(preserveSignedRegisters);
260
261        Input in = new ByteArrayInput(FileUtils.readFile(file));
262
263        HeaderItemSection.readFrom(1, in);
264        HeaderItem headerItem = HeaderItemSection.items.get(0);
265
266        in.setCursor(headerItem.getMapOffset());
267        MapSection.readFrom(1, in);
268
269        MapField[] mapEntries = MapSection.items.get(0).getMapEntries();
270        HashMap<Integer, MapField> mapMap = new HashMap<Integer, MapField>();
271        for (MapField mapField: mapEntries) {
272            mapMap.put(mapField.getSectionItemType().getMapValue(), mapField);
273        }
274
275        /**
276         * This defines the order in which the sections are read in. This is not
277         * necessarily the order in which the appear in the file.
278         */
279        //TODO: do we *need* to read them in a specific order, rather than the order that is in the file?
280        int[] sectionTypes = new int[] {
281            ItemType.TYPE_HEADER_ITEM.getMapValue(),
282            ItemType.TYPE_STRING_ID_ITEM.getMapValue(),
283            ItemType.TYPE_TYPE_ID_ITEM.getMapValue(),
284            ItemType.TYPE_PROTO_ID_ITEM.getMapValue(),
285            ItemType.TYPE_FIELD_ID_ITEM.getMapValue(),
286            ItemType.TYPE_METHOD_ID_ITEM.getMapValue(),
287            ItemType.TYPE_CLASS_DEF_ITEM.getMapValue(),
288            ItemType.TYPE_STRING_DATA_ITEM.getMapValue(),
289            ItemType.TYPE_ENCODED_ARRAY_ITEM.getMapValue(),
290            ItemType.TYPE_ANNOTATION_ITEM.getMapValue(),
291            ItemType.TYPE_ANNOTATION_SET_ITEM.getMapValue(),
292            ItemType.TYPE_ANNOTATION_SET_REF_LIST.getMapValue(),
293            ItemType.TYPE_ANNOTATIONS_DIRECTORY_ITEM.getMapValue(),
294            ItemType.TYPE_TYPE_LIST.getMapValue(),
295            ItemType.TYPE_CODE_ITEM.getMapValue(),
296            ItemType.TYPE_CLASS_DATA_ITEM.getMapValue(),
297            ItemType.TYPE_DEBUG_INFO_ITEM.getMapValue(),
298            ItemType.TYPE_MAP_LIST.getMapValue()
299        };
300
301        for (int sectionType: sectionTypes) {
302            MapField mapField = mapMap.get(sectionType);
303            if (mapField != null) {
304                Section section = sectionsByType.get(mapField.getSectionItemType());
305                if (section != null) {
306                    in.setCursor(mapField.getCachedSectionOffset());
307                    section.readFrom(mapField.getCachedSectionSize(), in);
308                }
309            }
310        }
311    }
312
313    /**
314     * Constructs a new, blank dex file. Classes can be added to this dex file by calling
315     * the <code>Section.intern()</code> method of <code>ClassDefsSection</code>
316     */
317    public DexFile() {
318        this(true);
319
320        HeaderItemSection.intern(new HeaderItem(dexFile, 0));
321        MapSection.intern(new MapItem(dexFile));
322    }
323
324    /**
325     * Convenience method to retrieve the header item
326     * @return the header item
327     */
328    public HeaderItem getHeaderItem() {
329        return HeaderItemSection.getItems().get(0);
330    }
331
332    /**
333     * Convenience method to retrieve the map item
334     * @return the map item
335     */
336    public MapItem getMapItem() {
337        return MapSection.getItems().get(0);
338    }
339
340    /**
341     * Get the <code>Section</code> containing items of the same type as the given item
342     * @param item Get the <code>Section</code> that contains items of this type
343     * @param <T> The specific item subclass - inferred from the passed item
344     * @return the <code>Section</code> containing items of the same type as the given item
345     */
346    public <T extends Item> Section<T> getSectionForItem(T item) {
347        return sectionsByType.get(item.getItemType());
348    }
349
350    /**
351     * Get the <code>Section</code> containing items of the given type
352     * @param itemType the type of item
353     * @return the <code>Section</code> containing items of the given type
354     */
355    public Section getSectionForType(ItemType itemType) {
356        return sectionsByType.get(itemType);
357    }
358
359    /**
360     * Get a boolean value indicating whether this dex file preserved any signed
361     * registers in the debug info as it read the dex file in. By default, the dex file
362     * doesn't check whether the registers are encoded as unsigned or signed values.
363     *
364     * This does *not* affect the actual register value that is read in. The value is
365     * read correctly regardless
366     *
367     * This does affect whether any signed registers will retain the same encoding or be
368     * forced to the (correct) unsigned encoding when the dex file is written back out.
369     *
370     * See the discussion about signed register values in the documentation for
371     * <code>DexFile</code>
372     * @return a boolean indicating whether this dex file preserved any signed registers
373     * as it was read in
374     */
375    public boolean getPreserveSignedRegisters() {
376        return preserveSignedRegisters;
377    }
378
379    /**
380     * Get a boolean value indicating whether all items should be placed into a
381     * (possibly arbitrary) "canonical" ordering. If false, then only the items
382     * that must be ordered per the dex specification are sorted.
383     *
384     * When true, writing the dex file involves somewhat more overhead
385     *
386     * If both SortAllItems and Inplace are true, Inplace takes precedence
387     * @return a boolean value indicating whether all items should be sorted
388     */
389    public boolean getSortAllItems() {
390        return this.sortAllItems;
391    }
392
393    /**
394     * Set a boolean value indicating whether all items should be placed into a
395     * (possibly arbitrary) "canonical" ordering. If false, then only the items
396     * that must be ordered per the dex specification are sorted.
397     *
398     * When true, writing the dex file involves somewhat more overhead
399     *
400     * If both SortAllItems and Inplace are true, Inplace takes precedence
401     * @param value a boolean value indicating whether all items should be sorted
402     */
403    public void setSortAllItems(boolean value) {
404        this.sortAllItems = value;
405    }
406
407    /**
408     * Get a boolean value indicating whether items in this dex file should be
409     * written back out "in-place", or whether the normal layout logic should be
410     * applied.
411     *
412     * This should only be used for a dex file that has been read from an existing
413     * dex file, and no modifications have been made to the dex file. Otherwise,
414     * there is a good chance that the resulting dex file will be invalid due to
415     * items that aren't placed correctly
416     *
417     * If both SortAllItems and Inplace are true, Inplace takes precedence
418     * @return a boolean value indicating whether items in this dex file should be
419     * written back out in-place.
420     */
421    public boolean getInplace() {
422        return this.inplace;
423    }
424
425    /**
426     * Set a boolean value indicating whether items in this dex file should be
427     * written back out "in-place", or whether the normal layout logic should be
428     * applied.
429     *
430     * This should only be used for a dex file that has been read from an existing
431     * dex file, and no modifications have been made to the dex file. Otherwise,
432     * there is a good chance that the resulting dex file will be invalid due to
433     * items that aren't placed correctly
434     *
435     * If both SortAllItems and Inplace are true, Inplace takes precedence
436     * @param value a boolean value indicating whether items in this dex file should be
437     * written back out in-place.
438     */
439    public void setInplace(boolean value) {
440        this.inplace = value;
441    }
442
443    /**
444     * This method should be called before writing a dex file. It sorts the sections
445     * as needed or as indicated by <code>getSortAllItems()</code> and <code>getInplace()</code>,
446     * and then performs a pass through all of the items, finalizing the position (i.e.
447     * index and/or offset) of each item in the dex file.
448     *
449     * This step is needed primarily so that the indexes and offsets of all indexed and
450     * offsetted items are available when writing references to those items elsewhere.
451     */
452    public void place() {
453        HeaderItem headerItem = getHeaderItem();
454
455        int offset = HeaderItemSection.place(0);
456
457        int dataOffset;
458
459        for (IndexedSection indexedSection: indexedSections) {
460            offset = indexedSection.place(offset);
461        }
462
463        dataOffset = offset;
464        headerItem.setDataOffset(dataOffset);
465
466        //TODO: if inplace is true, we need to use the order of the sections as they were in the original file
467        for (OffsettedSection offsettedSection: offsettedSections) {
468            if (this.sortAllItems && !this.inplace) {
469                offsettedSection.sortSection();
470            }
471            offset = offsettedSection.place(offset);
472        }
473
474        offset = MapSection.place(offset);
475
476
477        headerItem.setFileSize(offset);
478        headerItem.setDataSize(offset - dataOffset);
479    }
480
481    /**
482     * Writes the dex file to the give <code>AnnotatedOutput</code> object. If
483     * <code>out.Annotates()</code> is true, then annotations that document the format
484     * of the dex file are written.
485     *
486     * You must call <code>place()</code> on this dex file, before calling this method
487     * @param out the AnnotatedOutput object to write the dex file and annotations to
488     *
489     * After calling this method, you should call <code>calcSignature()</code> and
490     * then <code>calcChecksum()</code> on the resulting byte array, to calculate the
491     * signature and checksum in the header
492     */
493    public void writeTo(AnnotatedOutput out) {
494        HeaderItemSection.writeTo(out);
495
496        for (IndexedSection indexedSection: indexedSections) {
497            indexedSection.writeTo(out);
498        }
499
500        for (OffsettedSection offsettedSection: offsettedSections) {
501            offsettedSection.writeTo(out);
502        }
503
504        MapSection.writeTo(out);
505    }
506
507    /**
508     * The <code>IndexedSection</code> containing the sole <code>HeaderItem</code> item. Use
509     * <code>getHeaderItem()</code> instead.
510     */
511    public final IndexedSection<HeaderItem> HeaderItemSection = new IndexedSection<HeaderItem>(this) {
512        protected HeaderItem make(int index) {
513            return new HeaderItem(dexFile, index);
514        }
515    };
516
517    /**
518     * The <code>IndexedSection</code> containing <code>StringIdItem</code> items
519     */
520    public final IndexedSection<StringIdItem> StringIdsSection = new IndexedSection<StringIdItem>(this) {
521        protected StringIdItem make(int index) {
522            return new StringIdItem(dexFile, index);
523        }
524    };
525
526    /**
527     * The <code>IndexedSection</code> containing <code>TypeIdItem</code> items
528     */
529    public final IndexedSection<TypeIdItem> TypeIdsSection = new IndexedSection<TypeIdItem>(this) {
530        protected TypeIdItem make(int index) {
531            return new TypeIdItem(dexFile, index);
532        }
533    };
534
535    /**
536     * The <code>IndexedSection</code> containing <code>ProtoIdItem</code> items
537     */
538    public final IndexedSection<ProtoIdItem> ProtoIdsSection = new IndexedSection<ProtoIdItem>(this) {
539        protected ProtoIdItem make(int index) {
540            return new ProtoIdItem(dexFile, index);
541        }
542    };
543
544    /**
545     * The <code>IndexedSection</code> containing <code>FieldIdItem</code> items
546     */
547    public final IndexedSection<FieldIdItem> FieldIdsSection = new IndexedSection<FieldIdItem>(this) {
548        protected FieldIdItem make(int index) {
549            return new FieldIdItem(dexFile, index);
550        }
551    };
552
553    /**
554     * The <code>IndexedSection</code> containing <code>MethodIdItem</code> items
555     */
556    public final IndexedSection<MethodIdItem> MethodIdsSection = new IndexedSection<MethodIdItem>(this) {
557        protected MethodIdItem make(int index) {
558            return new MethodIdItem(dexFile, index);
559        }
560    };
561
562    /**
563     * The <code>IndexedSection</code> containing <code>ClassDefItem</code> items
564     */
565    public final IndexedSection<ClassDefItem> ClassDefsSection = new IndexedSection<ClassDefItem>(this) {
566        protected ClassDefItem make(int index) {
567            return new ClassDefItem(dexFile, index);
568        }
569
570        public int place(int offset) {
571            if (dexFile.getInplace()) {
572                return super.place(offset);
573            }
574
575            int ret = ClassDefItem.placeClassDefItems(this, offset);
576
577            this.offset = items.get(0).getOffset();
578            return ret;
579        }
580    };
581
582    /**
583     * The <code>IndexedSection</code> containing the sole <code>MapItem</code>. Use
584     * <code>getMapItem()</code> instead
585     */
586    public final IndexedSection<MapItem> MapSection = new IndexedSection<MapItem>(this) {
587        protected MapItem make(int index) {
588            return new MapItem(dexFile, index);
589        }
590
591        public MapItem intern(MapItem item) {
592            this.items.add(item);
593            return item;
594        }
595    };
596
597    /**
598     * The <code>OffsettedSection</code> containing <code>TypeListItem</code> items
599     */
600    public final OffsettedSection<TypeListItem> TypeListsSection = new OffsettedSection<TypeListItem>(this) {
601        protected TypeListItem make(int offset) {
602            return new TypeListItem(dexFile, offset);
603        }
604    };
605
606    /**
607     * The <code>OffsettedSection</code> containing <code>AnnotationSetRefList</code> items
608     */
609    public final OffsettedSection<AnnotationSetRefList> AnnotationSetRefListsSection =
610            new OffsettedSection<AnnotationSetRefList>(this) {
611                protected AnnotationSetRefList make(int offset) {
612                    return new AnnotationSetRefList(dexFile, offset);
613                }
614            };
615
616    /**
617     * The <code>OffsettedSection</code> containing <code>AnnotationSetItem</code> items
618     */
619    public final OffsettedSection<AnnotationSetItem> AnnotationSetsSection =
620            new OffsettedSection<AnnotationSetItem>(this) {
621                protected AnnotationSetItem make(int offset) {
622                    return new AnnotationSetItem(dexFile, offset);
623                }
624            };
625
626    /**
627     * The <code>OffsettedSection</code> containing <code>ClassDataItem</code> items
628     */
629    public final OffsettedSection<ClassDataItem> ClassDataSection = new OffsettedSection<ClassDataItem>(this) {
630        protected ClassDataItem make(int offset) {
631            return new ClassDataItem(dexFile, offset);
632        }
633    };
634
635    /**
636     * The <code>OffsettedSection</code> containing <code>CodeItem</code> items
637     */
638    public final OffsettedSection<CodeItem> CodeItemsSection = new OffsettedSection<CodeItem>(this) {
639        protected CodeItem make(int offset) {
640            return new CodeItem(dexFile, offset);
641        }
642    };
643
644    /**
645     * The <code>OffsettedSection</code> containing <code>StringDataItem</code> items
646     */
647    public final OffsettedSection<StringDataItem> StringDataSection = new OffsettedSection<StringDataItem>(this) {
648        protected StringDataItem make(int offset) {
649            return new StringDataItem(dexFile, offset);
650        }
651    };
652
653    /**
654     * The <code>OffsettedSection</code> containing <code>DebugInfoItem</code> items
655     */
656    public final OffsettedSection<DebugInfoItem> DebugInfoItemsSection = new OffsettedSection<DebugInfoItem>(this) {
657        protected DebugInfoItem make(int offset) {
658            return new DebugInfoItem(dexFile, offset);
659        }
660    };
661
662    /**
663     * The <code>OffsettedSection</code> containing <code>AnnotationItem</code> items
664     */
665    public final OffsettedSection<AnnotationItem> AnnotationsSection = new OffsettedSection<AnnotationItem>(this) {
666        protected AnnotationItem make(int offset) {
667            return new AnnotationItem(dexFile, offset);
668        }
669    };
670
671    /**
672     * The <code>OffsettedSection</code> containing <code>EncodedArrayItem</code> items
673     */
674    public final OffsettedSection<EncodedArrayItem> EncodedArraysSection = new OffsettedSection<EncodedArrayItem>(this) {
675        protected EncodedArrayItem make(int offset) {
676            return new EncodedArrayItem(dexFile, offset);
677        }
678    };
679
680    /**
681     * The <code>OffsettedSection</code> containing <code>AnnotationDirectoryItem</code> items
682     */
683    public final OffsettedSection<AnnotationDirectoryItem> AnnotationDirectoriesSection =
684            new OffsettedSection<AnnotationDirectoryItem>(this) {
685                protected AnnotationDirectoryItem make(int offset) {
686                    return new AnnotationDirectoryItem(dexFile, offset);
687                }
688            };
689
690
691    /**
692     * Calculates the signature for the dex file in the given byte array,
693     * and then writes the signature to the appropriate location in the header
694     * containing in the array
695     *
696     * @param bytes non-null; the bytes of the file
697     */
698    public static void calcSignature(byte[] bytes) {
699        MessageDigest md;
700
701        try {
702            md = MessageDigest.getInstance("SHA-1");
703        } catch (NoSuchAlgorithmException ex) {
704            throw new RuntimeException(ex);
705        }
706
707        md.update(bytes, 32, bytes.length - 32);
708
709        try {
710            int amt = md.digest(bytes, 12, 20);
711            if (amt != 20) {
712                throw new RuntimeException("unexpected digest write: " + amt +
713                                           " bytes");
714            }
715        } catch (DigestException ex) {
716            throw new RuntimeException(ex);
717        }
718    }
719
720    /**
721     * Calculates the checksum for the <code>.dex</code> file in the
722     * given array, and modify the array to contain it.
723     *
724     * @param bytes non-null; the bytes of the file
725     */
726    public static void calcChecksum(byte[] bytes) {
727        Adler32 a32 = new Adler32();
728
729        a32.update(bytes, 12, bytes.length - 12);
730
731        int sum = (int) a32.getValue();
732
733        bytes[8]  = (byte) sum;
734        bytes[9]  = (byte) (sum >> 8);
735        bytes[10] = (byte) (sum >> 16);
736        bytes[11] = (byte) (sum >> 24);
737    }
738}
739