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 }