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