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