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