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}