Home | History | Annotate | Download | only in dexlib
      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 
     29 package org.jf.dexlib;
     30 
     31 import org.jf.dexlib.Util.*;
     32 
     33 import java.io.*;
     34 import java.security.DigestException;
     35 import java.security.MessageDigest;
     36 import java.security.NoSuchAlgorithmException;
     37 import java.util.Arrays;
     38 import java.util.Collections;
     39 import java.util.Comparator;
     40 import java.util.zip.Adler32;
     41 import java.util.zip.ZipEntry;
     42 import 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  */
    102 public 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      * Is this file an odex file? This is only set when reading in an odex file
    168      */
    169     private boolean isOdex = false;
    170 
    171     private OdexHeader odexHeader;
    172     private OdexDependencies odexDependencies;
    173 
    174     private int dataOffset;
    175     private int dataSize;
    176     private int fileSize;
    177 
    178     /**
    179      * A private constructor containing common code to initialize the section maps and lists
    180      * @param preserveSignedRegisters If true, keep track of any registers in the debug information
    181      * @param skipInstructions If true, skip the instructions in any code item.
    182      * that are signed, so they will be written in the same format. See
    183      * <code>getPreserveSignedRegisters()</code>
    184      */
    185     private DexFile(boolean preserveSignedRegisters, boolean skipInstructions) {
    186         this.preserveSignedRegisters = preserveSignedRegisters;
    187         this.skipInstructions = skipInstructions;
    188 
    189         sectionsByType = new Section[] {
    190                 StringIdsSection,
    191                 TypeIdsSection,
    192                 ProtoIdsSection,
    193                 FieldIdsSection,
    194                 MethodIdsSection,
    195                 ClassDefsSection,
    196                 TypeListsSection,
    197                 AnnotationSetRefListsSection,
    198                 AnnotationSetsSection,
    199                 ClassDataSection,
    200                 CodeItemsSection,
    201                 AnnotationDirectoriesSection,
    202                 StringDataSection,
    203                 DebugInfoItemsSection,
    204                 AnnotationsSection,
    205                 EncodedArraysSection,
    206                 null,
    207                 null
    208         };
    209 
    210         indexedSections = new IndexedSection[] {
    211                 StringIdsSection,
    212                 TypeIdsSection,
    213                 ProtoIdsSection,
    214                 FieldIdsSection,
    215                 MethodIdsSection,
    216                 ClassDefsSection
    217         };
    218 
    219         offsettedSections = new OffsettedSection[] {
    220                 AnnotationSetRefListsSection,
    221                 AnnotationSetsSection,
    222                 CodeItemsSection,
    223                 AnnotationDirectoriesSection,
    224                 TypeListsSection,
    225                 StringDataSection,
    226                 AnnotationsSection,
    227                 EncodedArraysSection,
    228                 ClassDataSection,
    229                 DebugInfoItemsSection
    230         };
    231     }
    232 
    233 
    234     /**
    235      * Construct a new DexFile instance by reading in the given dex file.
    236      * @param file The dex file to read in
    237      * @throws IOException if an IOException occurs
    238      */
    239     public DexFile(String file)
    240             throws IOException {
    241         this(new File(file), true, false);
    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      * @param skipInstructions If true, skip the instructions in any code item.
    252      * <code>getPreserveSignedRegisters()</code>
    253      * @throws IOException if an IOException occurs
    254      */
    255     public DexFile(String file, boolean preserveSignedRegisters, boolean skipInstructions)
    256             throws IOException {
    257         this(new File(file), preserveSignedRegisters, skipInstructions);
    258     }
    259 
    260     /**
    261      * Construct a new DexFile instance by reading in the given dex file.
    262      * @param file The dex file to read in
    263      * @throws IOException if an IOException occurs
    264      */
    265     public DexFile(File file)
    266             throws IOException {
    267         this(file, true, false);
    268     }
    269 
    270     /**
    271      * Construct a new DexFile instance by reading in the given dex file,
    272      * and optionally keep track of any registers in the debug information that are signed,
    273      * so they will be written in the same format.
    274      * @param file The dex file to read in
    275      * @param preserveSignedRegisters If true, keep track of any registers in the debug information
    276      * that are signed, so they will be written in the same format.
    277      * @param skipInstructions If true, skip the instructions in any code item.
    278      * @see #getPreserveSignedRegisters
    279      * @throws IOException if an IOException occurs
    280      */
    281     public DexFile(File file, boolean preserveSignedRegisters, boolean skipInstructions)
    282             throws IOException {
    283         this(preserveSignedRegisters, skipInstructions);
    284 
    285         long fileLength;
    286         byte[] magic = FileUtils.readFile(file, 0, 8);
    287 
    288         InputStream inputStream = null;
    289         Input in = null;
    290         ZipFile zipFile = null;
    291 
    292         try {
    293             //do we have a zip file?
    294             if (magic[0] == 0x50 && magic[1] == 0x4B) {
    295                 zipFile = new ZipFile(file);
    296                 ZipEntry zipEntry = zipFile.getEntry("classes.dex");
    297                 if (zipEntry == null) {
    298                     throw new NoClassesDexException("zip file " + file.getName() + " does not contain a classes.dex " +
    299                             "file");
    300                 }
    301                 fileLength = zipEntry.getSize();
    302                 if (fileLength < 40) {
    303                     throw new RuntimeException("The classes.dex file in " + file.getName() + " is too small to be a" +
    304                             " valid dex file");
    305                 } else if (fileLength > Integer.MAX_VALUE) {
    306                     throw new RuntimeException("The classes.dex file in " + file.getName() + " is too large to read in");
    307                 }
    308                 inputStream = new BufferedInputStream(zipFile.getInputStream(zipEntry));
    309 
    310                 inputStream.mark(8);
    311                 for (int i=0; i<8; i++) {
    312                     magic[i] = (byte)inputStream.read();
    313                 }
    314                 inputStream.reset();
    315             } else {
    316                 fileLength = file.length();
    317                 if (fileLength < 40) {
    318                     throw new RuntimeException(file.getName() + " is too small to be a valid dex file");
    319                 }
    320                 if (fileLength < 40) {
    321                     throw new RuntimeException(file.getName() + " is too small to be a valid dex file");
    322                 } else if (fileLength > Integer.MAX_VALUE) {
    323                     throw new RuntimeException(file.getName() + " is too large to read in");
    324                 }
    325                 inputStream = new FileInputStream(file);
    326             }
    327 
    328             byte[] dexMagic, odexMagic;
    329             boolean isDex = false;
    330             this.isOdex = false;
    331 
    332             for (int i=0; i<HeaderItem.MAGIC_VALUES.length; i++) {
    333                 byte[] magic_value = HeaderItem.MAGIC_VALUES[i];
    334                 if (Arrays.equals(magic, magic_value)) {
    335                     isDex = true;
    336                     break;
    337                 }
    338             }
    339             if (!isDex) {
    340                 if (Arrays.equals(magic, OdexHeader.MAGIC_35)) {
    341                     isOdex = true;
    342                 } else if (Arrays.equals(magic, OdexHeader.MAGIC_36)) {
    343                     isOdex = true;
    344                 }
    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.this.getInplace()) {
    771                 return super.placeAt(offset);
    772             }
    773 
    774             int ret = ClassDefItem.placeClassDefItems(this, offset);
    775 
    776             Collections.sort(this.items);
    777 
    778             this.offset = items.get(0).getOffset();
    779             return ret;
    780         }
    781 
    782         protected void sortSection() {
    783             // Do nothing. Sorting is handled by ClassDefItem.ClassDefPlacer, during placement
    784         }
    785     };
    786 
    787     /**
    788      * The <code>OffsettedSection</code> containing <code>TypeListItem</code> items
    789      */
    790     public final OffsettedSection<TypeListItem> TypeListsSection =
    791             new OffsettedSection<TypeListItem>(this, ItemType.TYPE_TYPE_LIST);
    792 
    793     /**
    794      * The <code>OffsettedSection</code> containing <code>AnnotationSetRefList</code> items
    795      */
    796     public final OffsettedSection<AnnotationSetRefList> AnnotationSetRefListsSection =
    797             new OffsettedSection<AnnotationSetRefList>(this, ItemType.TYPE_ANNOTATION_SET_REF_LIST);
    798 
    799     /**
    800      * The <code>OffsettedSection</code> containing <code>AnnotationSetItem</code> items
    801      */
    802     public final OffsettedSection<AnnotationSetItem> AnnotationSetsSection =
    803             new OffsettedSection<AnnotationSetItem>(this, ItemType.TYPE_ANNOTATION_SET_ITEM);
    804 
    805     /**
    806      * The <code>OffsettedSection</code> containing <code>ClassDataItem</code> items
    807      */
    808     public final OffsettedSection<ClassDataItem> ClassDataSection =
    809             new OffsettedSection<ClassDataItem>(this, ItemType.TYPE_CLASS_DATA_ITEM);
    810 
    811     /**
    812      * The <code>OffsettedSection</code> containing <code>CodeItem</code> items
    813      */
    814     public final OffsettedSection<CodeItem> CodeItemsSection =
    815             new OffsettedSection<CodeItem>(this, ItemType.TYPE_CODE_ITEM);
    816 
    817     /**
    818      * The <code>OffsettedSection</code> containing <code>StringDataItem</code> items
    819      */
    820     public final OffsettedSection<StringDataItem> StringDataSection =
    821             new OffsettedSection<StringDataItem>(this, ItemType.TYPE_STRING_DATA_ITEM);
    822 
    823     /**
    824      * The <code>OffsettedSection</code> containing <code>DebugInfoItem</code> items
    825      */
    826     public final OffsettedSection<DebugInfoItem> DebugInfoItemsSection =
    827             new OffsettedSection<DebugInfoItem>(this, ItemType.TYPE_DEBUG_INFO_ITEM);
    828 
    829     /**
    830      * The <code>OffsettedSection</code> containing <code>AnnotationItem</code> items
    831      */
    832     public final OffsettedSection<AnnotationItem> AnnotationsSection =
    833             new OffsettedSection<AnnotationItem>(this, ItemType.TYPE_ANNOTATION_ITEM);
    834 
    835     /**
    836      * The <code>OffsettedSection</code> containing <code>EncodedArrayItem</code> items
    837      */
    838     public final OffsettedSection<EncodedArrayItem> EncodedArraysSection =
    839             new OffsettedSection<EncodedArrayItem>(this, ItemType.TYPE_ENCODED_ARRAY_ITEM);
    840 
    841     /**
    842      * The <code>OffsettedSection</code> containing <code>AnnotationDirectoryItem</code> items
    843      */
    844     public final OffsettedSection<AnnotationDirectoryItem> AnnotationDirectoriesSection =
    845             new OffsettedSection<AnnotationDirectoryItem>(this, ItemType.TYPE_ANNOTATIONS_DIRECTORY_ITEM);
    846 
    847 
    848     /**
    849      * Calculates the signature for the dex file in the given byte array,
    850      * and then writes the signature to the appropriate location in the header
    851      * containing in the array
    852      *
    853      * @param bytes non-null; the bytes of the file
    854      */
    855     public static void calcSignature(byte[] bytes) {
    856         MessageDigest md;
    857 
    858         try {
    859             md = MessageDigest.getInstance("SHA-1");
    860         } catch (NoSuchAlgorithmException ex) {
    861             throw new RuntimeException(ex);
    862         }
    863 
    864         md.update(bytes, 32, bytes.length - 32);
    865 
    866         try {
    867             int amt = md.digest(bytes, 12, 20);
    868             if (amt != 20) {
    869                 throw new RuntimeException("unexpected digest write: " + amt +
    870                                            " bytes");
    871             }
    872         } catch (DigestException ex) {
    873             throw new RuntimeException(ex);
    874         }
    875     }
    876 
    877     /**
    878      * Calculates the checksum for the <code>.dex</code> file in the
    879      * given array, and modify the array to contain it.
    880      *
    881      * @param bytes non-null; the bytes of the file
    882      */
    883     public static void calcChecksum(byte[] bytes) {
    884         Adler32 a32 = new Adler32();
    885 
    886         a32.update(bytes, 12, bytes.length - 12);
    887 
    888         int sum = (int) a32.getValue();
    889 
    890         bytes[8]  = (byte) sum;
    891         bytes[9]  = (byte) (sum >> 8);
    892         bytes[10] = (byte) (sum >> 16);
    893         bytes[11] = (byte) (sum >> 24);
    894     }
    895 
    896     public static class NoClassesDexException extends ExceptionWithContext {
    897         public NoClassesDexException(String message) {
    898             super(message);
    899         }
    900     }
    901 }