1 /* 2 * Copyright 2013, Google Inc. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * * Redistributions in binary form must reproduce the above 12 * copyright notice, this list of conditions and the following disclaimer 13 * in the documentation and/or other materials provided with the 14 * distribution. 15 * * Neither the name of Google Inc. nor the names of its 16 * contributors may be used to endorse or promote products derived from 17 * this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 package org.jf.dexlib2.writer; 33 34 import com.google.common.collect.Iterables; 35 import com.google.common.collect.Lists; 36 import com.google.common.collect.Maps; 37 import com.google.common.collect.Ordering; 38 import com.google.common.primitives.Ints; 39 import org.jf.dexlib2.*; 40 import org.jf.dexlib2.base.BaseAnnotation; 41 import org.jf.dexlib2.base.BaseAnnotationElement; 42 import org.jf.dexlib2.builder.MutableMethodImplementation; 43 import org.jf.dexlib2.builder.instruction.BuilderInstruction31c; 44 import org.jf.dexlib2.dexbacked.raw.*; 45 import org.jf.dexlib2.iface.Annotation; 46 import org.jf.dexlib2.iface.ExceptionHandler; 47 import org.jf.dexlib2.iface.TryBlock; 48 import org.jf.dexlib2.iface.debug.DebugItem; 49 import org.jf.dexlib2.iface.debug.LineNumber; 50 import org.jf.dexlib2.iface.instruction.Instruction; 51 import org.jf.dexlib2.iface.instruction.OneRegisterInstruction; 52 import org.jf.dexlib2.iface.instruction.ReferenceInstruction; 53 import org.jf.dexlib2.iface.instruction.VariableRegisterInstruction; 54 import org.jf.dexlib2.iface.instruction.formats.*; 55 import org.jf.dexlib2.iface.reference.*; 56 import org.jf.dexlib2.util.InstructionUtil; 57 import org.jf.dexlib2.util.MethodUtil; 58 import org.jf.dexlib2.util.ReferenceUtil; 59 import org.jf.dexlib2.writer.io.DeferredOutputStream; 60 import org.jf.dexlib2.writer.io.DeferredOutputStreamFactory; 61 import org.jf.dexlib2.writer.io.DexDataStore; 62 import org.jf.dexlib2.writer.io.MemoryDeferredOutputStream; 63 import org.jf.dexlib2.writer.util.TryListBuilder; 64 import org.jf.util.ExceptionWithContext; 65 66 import javax.annotation.Nonnull; 67 import javax.annotation.Nullable; 68 import java.io.ByteArrayOutputStream; 69 import java.io.IOException; 70 import java.io.InputStream; 71 import java.io.OutputStream; 72 import java.nio.ByteBuffer; 73 import java.nio.ByteOrder; 74 import java.security.MessageDigest; 75 import java.security.NoSuchAlgorithmException; 76 import java.util.*; 77 import java.util.Map.Entry; 78 import java.util.zip.Adler32; 79 80 public abstract class DexWriter< 81 StringKey extends CharSequence, StringRef extends StringReference, TypeKey extends CharSequence, 82 TypeRef extends TypeReference, ProtoRefKey extends MethodProtoReference, 83 FieldRefKey extends FieldReference, MethodRefKey extends MethodReference, 84 ClassKey extends Comparable<? super ClassKey>, 85 CallSiteKey extends CallSiteReference, 86 MethodHandleKey extends MethodHandleReference, 87 AnnotationKey extends Annotation, AnnotationSetKey, 88 TypeListKey, 89 FieldKey, MethodKey, 90 EncodedArrayKey, 91 EncodedValue, 92 AnnotationElement extends org.jf.dexlib2.iface.AnnotationElement, 93 StringSectionType extends StringSection<StringKey, StringRef>, 94 TypeSectionType extends TypeSection<StringKey, TypeKey, TypeRef>, 95 ProtoSectionType extends ProtoSection<StringKey, TypeKey, ProtoRefKey, TypeListKey>, 96 FieldSectionType extends FieldSection<StringKey, TypeKey, FieldRefKey, FieldKey>, 97 MethodSectionType extends MethodSection<StringKey, TypeKey, ProtoRefKey, MethodRefKey, MethodKey>, 98 ClassSectionType extends ClassSection<StringKey, TypeKey, TypeListKey, ClassKey, FieldKey, MethodKey, 99 AnnotationSetKey, EncodedArrayKey>, 100 CallSiteSectionType extends CallSiteSection<CallSiteKey, EncodedArrayKey>, 101 MethodHandleSectionType extends MethodHandleSection<MethodHandleKey, FieldRefKey, MethodRefKey>, 102 TypeListSectionType extends TypeListSection<TypeKey, TypeListKey>, 103 AnnotationSectionType extends AnnotationSection<StringKey, TypeKey, AnnotationKey, AnnotationElement, 104 EncodedValue>, 105 AnnotationSetSectionType extends AnnotationSetSection<AnnotationKey, AnnotationSetKey>, 106 EncodedArraySectionType extends EncodedArraySection<EncodedArrayKey, EncodedValue>> { 107 public static final int NO_INDEX = -1; 108 public static final int NO_OFFSET = 0; 109 110 public static final int MAX_POOL_SIZE = (1 << 16); 111 112 protected final Opcodes opcodes; 113 114 protected int stringIndexSectionOffset = NO_OFFSET; 115 protected int typeSectionOffset = NO_OFFSET; 116 protected int protoSectionOffset = NO_OFFSET; 117 protected int fieldSectionOffset = NO_OFFSET; 118 protected int methodSectionOffset = NO_OFFSET; 119 protected int classIndexSectionOffset = NO_OFFSET; 120 protected int callSiteSectionOffset = NO_OFFSET; 121 protected int methodHandleSectionOffset = NO_OFFSET; 122 123 protected int stringDataSectionOffset = NO_OFFSET; 124 protected int classDataSectionOffset = NO_OFFSET; 125 protected int typeListSectionOffset = NO_OFFSET; 126 protected int encodedArraySectionOffset = NO_OFFSET; 127 protected int annotationSectionOffset = NO_OFFSET; 128 protected int annotationSetSectionOffset = NO_OFFSET; 129 protected int annotationSetRefSectionOffset = NO_OFFSET; 130 protected int annotationDirectorySectionOffset = NO_OFFSET; 131 protected int debugSectionOffset = NO_OFFSET; 132 protected int codeSectionOffset = NO_OFFSET; 133 protected int mapSectionOffset = NO_OFFSET; 134 135 protected int numAnnotationSetRefItems = 0; 136 protected int numAnnotationDirectoryItems = 0; 137 protected int numDebugInfoItems = 0; 138 protected int numCodeItemItems = 0; 139 protected int numClassDataItems = 0; 140 141 // The sections defined here must be kept in sync with these section arrays: 142 // - DexWriter.overflowableSections 143 // - DexPool.sections 144 145 public final StringSectionType stringSection; 146 public final TypeSectionType typeSection; 147 public final ProtoSectionType protoSection; 148 public final FieldSectionType fieldSection; 149 public final MethodSectionType methodSection; 150 public final ClassSectionType classSection; 151 public final CallSiteSectionType callSiteSection; 152 public final MethodHandleSectionType methodHandleSection; 153 154 public final TypeListSectionType typeListSection; 155 public final AnnotationSectionType annotationSection; 156 public final AnnotationSetSectionType annotationSetSection; 157 public final EncodedArraySectionType encodedArraySection; 158 159 private final IndexSection<?>[] overflowableSections; 160 DexWriter(Opcodes opcodes)161 protected DexWriter(Opcodes opcodes) { 162 this.opcodes = opcodes; 163 164 SectionProvider sectionProvider = getSectionProvider(); 165 this.stringSection = sectionProvider.getStringSection(); 166 this.typeSection = sectionProvider.getTypeSection(); 167 this.protoSection = sectionProvider.getProtoSection(); 168 this.fieldSection = sectionProvider.getFieldSection(); 169 this.methodSection = sectionProvider.getMethodSection(); 170 this.classSection = sectionProvider.getClassSection(); 171 this.callSiteSection = sectionProvider.getCallSiteSection(); 172 this.methodHandleSection = sectionProvider.getMethodHandleSection(); 173 this.typeListSection = sectionProvider.getTypeListSection(); 174 this.annotationSection = sectionProvider.getAnnotationSection(); 175 this.annotationSetSection = sectionProvider.getAnnotationSetSection(); 176 this.encodedArraySection = sectionProvider.getEncodedArraySection(); 177 178 overflowableSections = new IndexSection<?>[] { 179 //stringSection, // supports jumbo indexes 180 typeSection, 181 protoSection, 182 fieldSection, 183 methodSection, 184 //classSection, // redundant check: cannot be larger than typeSection 185 callSiteSection, 186 methodHandleSection, 187 }; 188 } 189 getSectionProvider()190 @Nonnull protected abstract SectionProvider getSectionProvider(); 191 writeEncodedValue(@onnull InternalEncodedValueWriter writer, @Nonnull EncodedValue encodedValue)192 protected abstract void writeEncodedValue(@Nonnull InternalEncodedValueWriter writer, 193 @Nonnull EncodedValue encodedValue) throws IOException; 194 195 private Comparator<Map.Entry<? extends CallSiteKey, Integer>> callSiteComparator = 196 new Comparator<Entry<? extends CallSiteKey, Integer>>() { 197 @Override 198 public int compare(Entry<? extends CallSiteKey, Integer> o1, Entry<? extends CallSiteKey, Integer> o2) { 199 int offset1 = encodedArraySection.getItemOffset(callSiteSection.getEncodedCallSite(o1.getKey())); 200 int offset2 = encodedArraySection.getItemOffset(callSiteSection.getEncodedCallSite(o2.getKey())); 201 return Ints.compare(offset1, offset2); 202 } 203 }; 204 205 private static Comparator<Map.Entry> toStringKeyComparator = 206 new Comparator<Map.Entry>() { 207 @Override public int compare(Entry o1, Entry o2) { 208 return o1.getKey().toString().compareTo(o2.getKey().toString()); 209 } 210 }; 211 comparableKeyComparator()212 private static <T extends Comparable<? super T>> Comparator<Map.Entry<? extends T, ?>> comparableKeyComparator() { 213 return new Comparator<Entry<? extends T, ?>>() { 214 @Override public int compare(Entry<? extends T, ?> o1, Entry<? extends T, ?> o2) { 215 return o1.getKey().compareTo(o2.getKey()); 216 } 217 }; 218 } 219 220 protected class InternalEncodedValueWriter extends EncodedValueWriter<StringKey, TypeKey, FieldRefKey, MethodRefKey, 221 AnnotationElement, ProtoRefKey, MethodHandleKey, EncodedValue> { 222 private InternalEncodedValueWriter(@Nonnull DexDataWriter writer) { 223 super(writer, stringSection, typeSection, fieldSection, methodSection, protoSection, methodHandleSection, 224 annotationSection); 225 } 226 227 @Override protected void writeEncodedValue(@Nonnull EncodedValue encodedValue) throws IOException { 228 DexWriter.this.writeEncodedValue(this, encodedValue); 229 } 230 } 231 232 private int getDataSectionOffset() { 233 return HeaderItem.ITEM_SIZE + 234 stringSection.getItemCount() * StringIdItem.ITEM_SIZE + 235 typeSection.getItemCount() * TypeIdItem.ITEM_SIZE + 236 protoSection.getItemCount() * ProtoIdItem.ITEM_SIZE + 237 fieldSection.getItemCount() * FieldIdItem.ITEM_SIZE + 238 methodSection.getItemCount() * MethodIdItem.ITEM_SIZE + 239 classSection.getItemCount() * ClassDefItem.ITEM_SIZE + 240 callSiteSection.getItemCount() * CallSiteIdItem.ITEM_SIZE + 241 methodHandleSection.getItemCount() * MethodHandleItem.ITEM_SIZE; 242 } 243 244 @Nonnull 245 public List<String> getMethodReferences() { 246 List<String> methodReferences = Lists.newArrayList(); 247 for (Entry<? extends MethodRefKey, Integer> methodReference: methodSection.getItems()) { 248 methodReferences.add(ReferenceUtil.getMethodDescriptor(methodReference.getKey())); 249 } 250 return methodReferences; 251 } 252 253 @Nonnull 254 public List<String> getFieldReferences() { 255 List<String> fieldReferences = Lists.newArrayList(); 256 for (Entry<? extends FieldRefKey, Integer> fieldReference: fieldSection.getItems()) { 257 fieldReferences.add(ReferenceUtil.getFieldDescriptor(fieldReference.getKey())); 258 } 259 return fieldReferences; 260 } 261 262 @Nonnull 263 public List<String> getTypeReferences() { 264 List<String> classReferences = Lists.newArrayList(); 265 for (Entry<? extends TypeKey, Integer> typeReference: typeSection.getItems()) { 266 classReferences.add(typeReference.getKey().toString()); 267 } 268 return classReferences; 269 } 270 271 /** 272 * Checks whether any of the size-sensitive constant pools have overflowed and have more than 64Ki entries. 273 * 274 * Note that even if this returns true, it may still be possible to successfully write the dex file, if the 275 * overflowed items are not referenced anywhere that uses a 16-bit index. 276 * 277 * @return true if any of the size-sensitive constant pools have overflowed 278 */ 279 public boolean hasOverflowed() { 280 return hasOverflowed(MAX_POOL_SIZE); 281 } 282 283 /** 284 * Checks whether any of the size-sensitive constant pools have more than the supplied maximum number of entries. 285 * 286 * @param maxPoolSize the maximum number of entries allowed in any of the size-sensitive constant pools 287 * @return true if any of the size-sensitive constant pools have overflowed the supplied size limit 288 */ 289 public boolean hasOverflowed(int maxPoolSize) { 290 for (IndexSection section: overflowableSections) { 291 if (section.getItemCount() > maxPoolSize) return true; 292 } 293 return false; 294 } 295 296 public void writeTo(@Nonnull DexDataStore dest) throws IOException { 297 this.writeTo(dest, MemoryDeferredOutputStream.getFactory()); 298 } 299 300 public void writeTo(@Nonnull DexDataStore dest, 301 @Nonnull DeferredOutputStreamFactory tempFactory) throws IOException { 302 try { 303 int dataSectionOffset = getDataSectionOffset(); 304 DexDataWriter headerWriter = outputAt(dest, 0); 305 DexDataWriter indexWriter = outputAt(dest, HeaderItem.ITEM_SIZE); 306 DexDataWriter offsetWriter = outputAt(dest, dataSectionOffset); 307 try { 308 writeStrings(indexWriter, offsetWriter); 309 writeTypes(indexWriter); 310 writeTypeLists(offsetWriter); 311 writeProtos(indexWriter); 312 writeFields(indexWriter); 313 writeMethods(indexWriter); 314 315 // encoded arrays depend on method handles.. 316 DexDataWriter methodHandleWriter = outputAt(dest, indexWriter.getPosition() + 317 classSection.getItemCount() * ClassDefItem.ITEM_SIZE + 318 callSiteSection.getItemCount() * CallSiteIdItem.ITEM_SIZE); 319 try { 320 writeMethodHandles(methodHandleWriter); 321 } finally { 322 methodHandleWriter.close(); 323 } 324 325 // call sites depend on encoded arrays.. 326 writeEncodedArrays(offsetWriter); 327 328 // class defs depend on method handles and call sites.. 329 DexDataWriter callSiteWriter = outputAt(dest, indexWriter.getPosition() + 330 classSection.getItemCount() * ClassDefItem.ITEM_SIZE); 331 try { 332 writeCallSites(callSiteWriter); 333 } finally { 334 callSiteWriter.close(); 335 } 336 337 writeAnnotations(offsetWriter); 338 writeAnnotationSets(offsetWriter); 339 writeAnnotationSetRefs(offsetWriter); 340 writeAnnotationDirectories(offsetWriter); 341 writeDebugAndCodeItems(offsetWriter, tempFactory.makeDeferredOutputStream()); 342 writeClasses(indexWriter, offsetWriter); 343 344 writeMapItem(offsetWriter); 345 writeHeader(headerWriter, dataSectionOffset, offsetWriter.getPosition()); 346 } finally { 347 headerWriter.close(); 348 indexWriter.close(); 349 offsetWriter.close(); 350 } 351 updateSignature(dest); 352 updateChecksum(dest); 353 } finally { 354 dest.close(); 355 } 356 } 357 358 private void updateSignature(@Nonnull DexDataStore dataStore) throws IOException { 359 MessageDigest md; 360 try { 361 md = MessageDigest.getInstance("SHA-1"); 362 } catch (NoSuchAlgorithmException ex) { 363 throw new RuntimeException(ex); 364 } 365 366 byte[] buffer = new byte[4 * 1024]; 367 InputStream input = dataStore.readAt(HeaderItem.SIGNATURE_DATA_START_OFFSET); 368 int bytesRead = input.read(buffer); 369 while (bytesRead >= 0) { 370 md.update(buffer, 0, bytesRead); 371 bytesRead = input.read(buffer); 372 } 373 374 byte[] signature = md.digest(); 375 if (signature.length != HeaderItem.SIGNATURE_SIZE) { 376 throw new RuntimeException("unexpected digest write: " + signature.length + " bytes"); 377 } 378 379 // write signature 380 OutputStream output = dataStore.outputAt(HeaderItem.SIGNATURE_OFFSET); 381 output.write(signature); 382 output.close(); 383 } 384 385 private void updateChecksum(@Nonnull DexDataStore dataStore) throws IOException { 386 Adler32 a32 = new Adler32(); 387 388 byte[] buffer = new byte[4 * 1024]; 389 InputStream input = dataStore.readAt(HeaderItem.CHECKSUM_DATA_START_OFFSET); 390 int bytesRead = input.read(buffer); 391 while (bytesRead >= 0) { 392 a32.update(buffer, 0, bytesRead); 393 bytesRead = input.read(buffer); 394 } 395 396 // write checksum, utilizing logic in DexWriter to write the integer value properly 397 OutputStream output = dataStore.outputAt(HeaderItem.CHECKSUM_OFFSET); 398 DexDataWriter.writeInt(output, (int)a32.getValue()); 399 output.close(); 400 } 401 402 private static DexDataWriter outputAt(DexDataStore dataStore, int filePosition) throws IOException { 403 return new DexDataWriter(dataStore.outputAt(filePosition), filePosition); 404 } 405 406 private void writeStrings(@Nonnull DexDataWriter indexWriter, @Nonnull DexDataWriter offsetWriter) throws IOException { 407 stringIndexSectionOffset = indexWriter.getPosition(); 408 stringDataSectionOffset = offsetWriter.getPosition(); 409 int index = 0; 410 List<Entry<? extends StringKey, Integer>> stringEntries = Lists.newArrayList(stringSection.getItems()); 411 Collections.sort(stringEntries, toStringKeyComparator); 412 413 for (Map.Entry<? extends StringKey, Integer> entry: stringEntries) { 414 entry.setValue(index++); 415 indexWriter.writeInt(offsetWriter.getPosition()); 416 String stringValue = entry.getKey().toString(); 417 offsetWriter.writeUleb128(stringValue.length()); 418 offsetWriter.writeString(stringValue); 419 offsetWriter.write(0); 420 } 421 } 422 423 private void writeTypes(@Nonnull DexDataWriter writer) throws IOException { 424 typeSectionOffset = writer.getPosition(); 425 int index = 0; 426 427 List<Map.Entry<? extends TypeKey, Integer>> typeEntries = Lists.newArrayList(typeSection.getItems()); 428 Collections.sort(typeEntries, toStringKeyComparator); 429 430 for (Map.Entry<? extends TypeKey, Integer> entry : typeEntries) { 431 entry.setValue(index++); 432 writer.writeInt(stringSection.getItemIndex(typeSection.getString(entry.getKey()))); 433 } 434 } 435 436 private void writeProtos(@Nonnull DexDataWriter writer) throws IOException { 437 protoSectionOffset = writer.getPosition(); 438 int index = 0; 439 440 List<Map.Entry<? extends ProtoRefKey, Integer>> protoEntries = Lists.newArrayList(protoSection.getItems()); 441 Collections.sort(protoEntries, DexWriter.<ProtoRefKey>comparableKeyComparator()); 442 443 for (Map.Entry<? extends ProtoRefKey, Integer> entry: protoEntries) { 444 entry.setValue(index++); 445 ProtoRefKey key = entry.getKey(); 446 writer.writeInt(stringSection.getItemIndex(protoSection.getShorty(key))); 447 writer.writeInt(typeSection.getItemIndex(protoSection.getReturnType(key))); 448 writer.writeInt(typeListSection.getNullableItemOffset(protoSection.getParameters(key))); 449 } 450 } 451 452 private void writeFields(@Nonnull DexDataWriter writer) throws IOException { 453 fieldSectionOffset = writer.getPosition(); 454 int index = 0; 455 456 List<Map.Entry<? extends FieldRefKey, Integer>> fieldEntries = Lists.newArrayList(fieldSection.getItems()); 457 Collections.sort(fieldEntries, DexWriter.<FieldRefKey>comparableKeyComparator()); 458 459 for (Map.Entry<? extends FieldRefKey, Integer> entry: fieldEntries) { 460 entry.setValue(index++); 461 FieldRefKey key = entry.getKey(); 462 writer.writeUshort(typeSection.getItemIndex(fieldSection.getDefiningClass(key))); 463 writer.writeUshort(typeSection.getItemIndex(fieldSection.getFieldType(key))); 464 writer.writeInt(stringSection.getItemIndex(fieldSection.getName(key))); 465 } 466 } 467 468 private void writeMethods(@Nonnull DexDataWriter writer) throws IOException { 469 methodSectionOffset = writer.getPosition(); 470 int index = 0; 471 472 List<Map.Entry<? extends MethodRefKey, Integer>> methodEntries = Lists.newArrayList(methodSection.getItems()); 473 Collections.sort(methodEntries, DexWriter.<MethodRefKey>comparableKeyComparator()); 474 475 for (Map.Entry<? extends MethodRefKey, Integer> entry: methodEntries) { 476 entry.setValue(index++); 477 MethodRefKey key = entry.getKey(); 478 writer.writeUshort(typeSection.getItemIndex(methodSection.getDefiningClass(key))); 479 writer.writeUshort(protoSection.getItemIndex(methodSection.getPrototype(key))); 480 writer.writeInt(stringSection.getItemIndex(methodSection.getName(key))); 481 } 482 } 483 484 private void writeClasses(@Nonnull DexDataWriter indexWriter, @Nonnull DexDataWriter offsetWriter) throws IOException { 485 classIndexSectionOffset = indexWriter.getPosition(); 486 classDataSectionOffset = offsetWriter.getPosition(); 487 488 List<Map.Entry<? extends ClassKey, Integer>> classEntries = Lists.newArrayList(classSection.getItems()); 489 Collections.sort(classEntries, DexWriter.<ClassKey>comparableKeyComparator()); 490 491 int index = 0; 492 for (Map.Entry<? extends ClassKey, Integer> key: classEntries) { 493 index = writeClass(indexWriter, offsetWriter, index, key); 494 } 495 } 496 497 /** 498 * Writes out the class_def_item and class_data_item for the given class. 499 * 500 * This will recursively write out any unwritten superclass/interface before writing the class itself, as per the 501 * dex specification. 502 * 503 * @return the index for the next class to be written 504 */ 505 private int writeClass(@Nonnull DexDataWriter indexWriter, @Nonnull DexDataWriter offsetWriter, 506 int nextIndex, @Nullable Map.Entry<? extends ClassKey, Integer> entry) throws IOException { 507 if (entry == null) { 508 // class does not exist in this dex file, cannot write it 509 return nextIndex; 510 } 511 512 if (entry.getValue() != NO_INDEX) { 513 // class has already been written, no need to write it 514 return nextIndex; 515 } 516 517 ClassKey key = entry.getKey(); 518 519 // set a bogus index, to make sure we don't recurse and double-write it 520 entry.setValue(0); 521 522 // first, try to write the superclass 523 Map.Entry<? extends ClassKey, Integer> superEntry = 524 classSection.getClassEntryByType(classSection.getSuperclass(key)); 525 nextIndex = writeClass(indexWriter, offsetWriter, nextIndex, superEntry); 526 527 // then, try to write interfaces 528 for (TypeKey interfaceTypeKey: typeListSection.getTypes(classSection.getInterfaces(key))) { 529 Map.Entry<? extends ClassKey, Integer> interfaceEntry = classSection.getClassEntryByType(interfaceTypeKey); 530 nextIndex = writeClass(indexWriter, offsetWriter, nextIndex, interfaceEntry); 531 } 532 533 // now set the index for real 534 entry.setValue(nextIndex++); 535 536 // and finally, write the class itself 537 // first, the class_def_item 538 indexWriter.writeInt(typeSection.getItemIndex(classSection.getType(key))); 539 indexWriter.writeInt(classSection.getAccessFlags(key)); 540 indexWriter.writeInt(typeSection.getNullableItemIndex(classSection.getSuperclass(key))); 541 indexWriter.writeInt(typeListSection.getNullableItemOffset(classSection.getInterfaces(key))); 542 indexWriter.writeInt(stringSection.getNullableItemIndex(classSection.getSourceFile(key))); 543 indexWriter.writeInt(classSection.getAnnotationDirectoryOffset(key)); 544 545 Collection<? extends FieldKey> staticFields = classSection.getSortedStaticFields(key); 546 Collection<? extends FieldKey> instanceFields = classSection.getSortedInstanceFields(key); 547 Collection<? extends MethodKey> directMethods = classSection.getSortedDirectMethods(key); 548 Collection<? extends MethodKey> virtualMethods = classSection.getSortedVirtualMethods(key); 549 boolean classHasData = staticFields.size() > 0 || 550 instanceFields.size() > 0 || 551 directMethods.size() > 0 || 552 virtualMethods.size() > 0; 553 554 if (classHasData) { 555 indexWriter.writeInt(offsetWriter.getPosition()); 556 } else { 557 indexWriter.writeInt(NO_OFFSET); 558 } 559 560 EncodedArrayKey staticInitializers = classSection.getStaticInitializers(key); 561 if (staticInitializers != null) { 562 indexWriter.writeInt(encodedArraySection.getItemOffset(staticInitializers)); 563 } else { 564 indexWriter.writeInt(NO_OFFSET); 565 } 566 567 // now write the class_data_item 568 if (classHasData) { 569 numClassDataItems++; 570 571 offsetWriter.writeUleb128(staticFields.size()); 572 offsetWriter.writeUleb128(instanceFields.size()); 573 offsetWriter.writeUleb128(directMethods.size()); 574 offsetWriter.writeUleb128(virtualMethods.size()); 575 576 writeEncodedFields(offsetWriter, staticFields); 577 writeEncodedFields(offsetWriter, instanceFields); 578 writeEncodedMethods(offsetWriter, directMethods); 579 writeEncodedMethods(offsetWriter, virtualMethods); 580 } 581 582 return nextIndex; 583 } 584 585 private void writeCallSites(DexDataWriter writer) throws IOException { 586 callSiteSectionOffset = writer.getPosition(); 587 588 List<Map.Entry<? extends CallSiteKey, Integer>> callSiteEntries = 589 Lists.newArrayList(callSiteSection.getItems()); 590 Collections.sort(callSiteEntries, callSiteComparator); 591 592 int index = 0; 593 594 for (Map.Entry<? extends CallSiteKey, Integer> callSite: callSiteEntries) { 595 callSite.setValue(index++); 596 writer.writeInt(encodedArraySection.getItemOffset(callSiteSection.getEncodedCallSite(callSite.getKey()))); 597 } 598 } 599 600 private void writeMethodHandles(DexDataWriter writer) throws IOException { 601 methodHandleSectionOffset = writer.getPosition(); 602 603 int index = 0; 604 605 for (Entry<? extends MethodHandleKey, Integer> entry: methodHandleSection.getItems()) { 606 entry.setValue(index++); 607 MethodHandleKey methodHandleReference = entry.getKey(); 608 writer.writeUshort(methodHandleReference.getMethodHandleType()); 609 writer.writeUshort(0); 610 int memberIndex; 611 switch (methodHandleReference.getMethodHandleType()) { 612 case MethodHandleType.STATIC_PUT: 613 case MethodHandleType.STATIC_GET: 614 case MethodHandleType.INSTANCE_PUT: 615 case MethodHandleType.INSTANCE_GET: 616 memberIndex = fieldSection.getItemIndex( 617 methodHandleSection.getFieldReference(methodHandleReference)); 618 break; 619 case MethodHandleType.INVOKE_STATIC: 620 case MethodHandleType.INVOKE_INSTANCE: 621 case MethodHandleType.INVOKE_CONSTRUCTOR: 622 case MethodHandleType.INVOKE_DIRECT: 623 case MethodHandleType.INVOKE_INTERFACE: 624 memberIndex = methodSection.getItemIndex( 625 methodHandleSection.getMethodReference(methodHandleReference)); 626 break; 627 default: 628 throw new ExceptionWithContext("Invalid method handle type: %d", 629 methodHandleReference.getMethodHandleType()); 630 } 631 632 writer.writeUshort(memberIndex); 633 writer.writeUshort(0); 634 } 635 } 636 637 private void writeEncodedFields(@Nonnull DexDataWriter writer, @Nonnull Collection<? extends FieldKey> fields) 638 throws IOException { 639 int prevIndex = 0; 640 for (FieldKey key: fields) { 641 int index = fieldSection.getFieldIndex(key); 642 writer.writeUleb128(index - prevIndex); 643 writer.writeUleb128(classSection.getFieldAccessFlags(key)); 644 prevIndex = index; 645 } 646 } 647 648 private void writeEncodedMethods(@Nonnull DexDataWriter writer, @Nonnull Collection<? extends MethodKey> methods) 649 throws IOException { 650 int prevIndex = 0; 651 for (MethodKey key: methods) { 652 int index = methodSection.getMethodIndex(key); 653 writer.writeUleb128(index-prevIndex); 654 writer.writeUleb128(classSection.getMethodAccessFlags(key)); 655 writer.writeUleb128(classSection.getCodeItemOffset(key)); 656 prevIndex = index; 657 } 658 } 659 660 private void writeTypeLists(@Nonnull DexDataWriter writer) throws IOException { 661 writer.align(); 662 typeListSectionOffset = writer.getPosition(); 663 for (Map.Entry<? extends TypeListKey, Integer> entry: typeListSection.getItems()) { 664 writer.align(); 665 entry.setValue(writer.getPosition()); 666 667 Collection<? extends TypeKey> types = typeListSection.getTypes(entry.getKey()); 668 writer.writeInt(types.size()); 669 for (TypeKey typeKey: types) { 670 writer.writeUshort(typeSection.getItemIndex(typeKey)); 671 } 672 } 673 } 674 675 private void writeEncodedArrays(@Nonnull DexDataWriter writer) throws IOException { 676 InternalEncodedValueWriter encodedValueWriter = new InternalEncodedValueWriter(writer); 677 encodedArraySectionOffset = writer.getPosition(); 678 679 for (Map.Entry<? extends EncodedArrayKey, Integer> entry: encodedArraySection.getItems()) { 680 entry.setValue(writer.getPosition()); 681 List<? extends EncodedValue> encodedArray = encodedArraySection.getEncodedValueList(entry.getKey()); 682 writer.writeUleb128(encodedArray.size()); 683 for (EncodedValue value: encodedArray) { 684 writeEncodedValue(encodedValueWriter, value); 685 } 686 } 687 } 688 689 private void writeAnnotations(@Nonnull DexDataWriter writer) throws IOException { 690 InternalEncodedValueWriter encodedValueWriter = new InternalEncodedValueWriter(writer); 691 692 annotationSectionOffset = writer.getPosition(); 693 for (Map.Entry<? extends AnnotationKey, Integer> entry: annotationSection.getItems()) { 694 entry.setValue(writer.getPosition()); 695 696 AnnotationKey key = entry.getKey(); 697 698 writer.writeUbyte(annotationSection.getVisibility(key)); 699 writer.writeUleb128(typeSection.getItemIndex(annotationSection.getType(key))); 700 701 Collection<? extends AnnotationElement> elements = Ordering.from(BaseAnnotationElement.BY_NAME) 702 .immutableSortedCopy(annotationSection.getElements(key)); 703 704 writer.writeUleb128(elements.size()); 705 706 for (AnnotationElement element: elements) { 707 writer.writeUleb128(stringSection.getItemIndex(annotationSection.getElementName(element))); 708 writeEncodedValue(encodedValueWriter, annotationSection.getElementValue(element)); 709 } 710 } 711 } 712 713 private void writeAnnotationSets(@Nonnull DexDataWriter writer) throws IOException { 714 writer.align(); 715 annotationSetSectionOffset = writer.getPosition(); 716 if (shouldCreateEmptyAnnotationSet()) { 717 writer.writeInt(0); 718 } 719 for (Map.Entry<? extends AnnotationSetKey, Integer> entry: annotationSetSection.getItems()) { 720 Collection<? extends AnnotationKey> annotations = Ordering.from(BaseAnnotation.BY_TYPE) 721 .immutableSortedCopy(annotationSetSection.getAnnotations(entry.getKey())); 722 723 writer.align(); 724 entry.setValue(writer.getPosition()); 725 writer.writeInt(annotations.size()); 726 for (AnnotationKey annotationKey: annotations) { 727 writer.writeInt(annotationSection.getItemOffset(annotationKey)); 728 } 729 } 730 } 731 732 private void writeAnnotationSetRefs(@Nonnull DexDataWriter writer) throws IOException { 733 writer.align(); 734 annotationSetRefSectionOffset = writer.getPosition(); 735 HashMap<List<? extends AnnotationSetKey>, Integer> internedItems = Maps.newHashMap(); 736 737 for (ClassKey classKey: classSection.getSortedClasses()) { 738 for (MethodKey methodKey: classSection.getSortedMethods(classKey)) { 739 List<? extends AnnotationSetKey> parameterAnnotations = classSection.getParameterAnnotations(methodKey); 740 if (parameterAnnotations != null) { 741 Integer prev = internedItems.get(parameterAnnotations); 742 if (prev != null) { 743 classSection.setAnnotationSetRefListOffset(methodKey, prev); 744 } else { 745 writer.align(); 746 int position = writer.getPosition(); 747 classSection.setAnnotationSetRefListOffset(methodKey, position); 748 internedItems.put(parameterAnnotations, position); 749 750 numAnnotationSetRefItems++; 751 752 writer.writeInt(parameterAnnotations.size()); 753 for (AnnotationSetKey annotationSetKey: parameterAnnotations) { 754 if (annotationSetSection.getAnnotations(annotationSetKey).size() > 0) { 755 writer.writeInt(annotationSetSection.getItemOffset(annotationSetKey)); 756 } else if (shouldCreateEmptyAnnotationSet()) { 757 writer.writeInt(annotationSetSectionOffset); 758 } else { 759 writer.writeInt(NO_OFFSET); 760 } 761 } 762 } 763 } 764 } 765 } 766 } 767 768 private void writeAnnotationDirectories(@Nonnull DexDataWriter writer) throws IOException { 769 writer.align(); 770 annotationDirectorySectionOffset = writer.getPosition(); 771 HashMap<AnnotationSetKey, Integer> internedItems = Maps.newHashMap(); 772 773 ByteBuffer tempBuffer = ByteBuffer.allocate(65536); 774 tempBuffer.order(ByteOrder.LITTLE_ENDIAN); 775 776 for (ClassKey key: classSection.getSortedClasses()) { 777 // first, we write the field/method/parameter items to a temporary buffer, so that we can get a count 778 // of each type, and determine if we even need to write an annotation directory for this class 779 780 Collection<? extends FieldKey> fields = classSection.getSortedFields(key); 781 Collection<? extends MethodKey> methods = classSection.getSortedMethods(key); 782 783 // this is how much space we'll need if every field and method has annotations. 784 int maxSize = fields.size() * 8 + methods.size() * 16; 785 if (maxSize > tempBuffer.capacity()) { 786 tempBuffer = ByteBuffer.allocate(maxSize); 787 tempBuffer.order(ByteOrder.LITTLE_ENDIAN); 788 } 789 790 tempBuffer.clear(); 791 792 int fieldAnnotations = 0; 793 int methodAnnotations = 0; 794 int parameterAnnotations = 0; 795 796 for (FieldKey field: fields) { 797 AnnotationSetKey fieldAnnotationsKey = classSection.getFieldAnnotations(field); 798 if (fieldAnnotationsKey != null) { 799 fieldAnnotations++; 800 tempBuffer.putInt(fieldSection.getFieldIndex(field)); 801 tempBuffer.putInt(annotationSetSection.getItemOffset(fieldAnnotationsKey)); 802 } 803 } 804 805 for (MethodKey method: methods) { 806 AnnotationSetKey methodAnnotationsKey = classSection.getMethodAnnotations(method); 807 if (methodAnnotationsKey != null) { 808 methodAnnotations++; 809 tempBuffer.putInt(methodSection.getMethodIndex(method)); 810 tempBuffer.putInt(annotationSetSection.getItemOffset(methodAnnotationsKey)); 811 } 812 } 813 814 for (MethodKey method: methods) { 815 int offset = classSection.getAnnotationSetRefListOffset(method); 816 if (offset != DexWriter.NO_OFFSET) { 817 parameterAnnotations++; 818 tempBuffer.putInt(methodSection.getMethodIndex(method)); 819 tempBuffer.putInt(offset); 820 } 821 } 822 823 // now, we finally know how many field/method/parameter annotations were written to the temp buffer 824 825 AnnotationSetKey classAnnotationKey = classSection.getClassAnnotations(key); 826 if (fieldAnnotations == 0 && methodAnnotations == 0 && parameterAnnotations == 0) { 827 if (classAnnotationKey != null) { 828 // This is an internable directory. Let's see if we've already written one like it 829 Integer directoryOffset = internedItems.get(classAnnotationKey); 830 if (directoryOffset != null) { 831 classSection.setAnnotationDirectoryOffset(key, directoryOffset); 832 continue; 833 } else { 834 internedItems.put(classAnnotationKey, writer.getPosition()); 835 } 836 } else { 837 continue; 838 } 839 } 840 841 // yep, we need to write it out 842 numAnnotationDirectoryItems++; 843 classSection.setAnnotationDirectoryOffset(key, writer.getPosition()); 844 845 writer.writeInt(annotationSetSection.getNullableItemOffset(classAnnotationKey)); 846 writer.writeInt(fieldAnnotations); 847 writer.writeInt(methodAnnotations); 848 writer.writeInt(parameterAnnotations); 849 writer.write(tempBuffer.array(), 0, tempBuffer.position()); 850 } 851 } 852 853 private static class CodeItemOffset<MethodKey> { 854 @Nonnull MethodKey method; 855 int codeOffset; 856 857 private CodeItemOffset(@Nonnull MethodKey method, int codeOffset) { 858 this.codeOffset = codeOffset; 859 this.method = method; 860 } 861 } 862 863 private void writeDebugAndCodeItems(@Nonnull DexDataWriter offsetWriter, 864 @Nonnull DeferredOutputStream temp) throws IOException { 865 ByteArrayOutputStream ehBuf = new ByteArrayOutputStream(); 866 debugSectionOffset = offsetWriter.getPosition(); 867 DebugWriter<StringKey, TypeKey> debugWriter = 868 new DebugWriter<StringKey, TypeKey>(stringSection, typeSection, offsetWriter); 869 870 DexDataWriter codeWriter = new DexDataWriter(temp, 0); 871 872 List<CodeItemOffset<MethodKey>> codeOffsets = Lists.newArrayList(); 873 874 for (ClassKey classKey: classSection.getSortedClasses()) { 875 Collection<? extends MethodKey> directMethods = classSection.getSortedDirectMethods(classKey); 876 Collection<? extends MethodKey> virtualMethods = classSection.getSortedVirtualMethods(classKey); 877 878 Iterable<MethodKey> methods = Iterables.concat(directMethods, virtualMethods); 879 880 for (MethodKey methodKey: methods) { 881 List<? extends TryBlock<? extends ExceptionHandler>> tryBlocks = 882 classSection.getTryBlocks(methodKey); 883 Iterable<? extends Instruction> instructions = classSection.getInstructions(methodKey); 884 Iterable<? extends DebugItem> debugItems = classSection.getDebugItems(methodKey); 885 886 if (instructions != null && stringSection.hasJumboIndexes()) { 887 boolean needsFix = false; 888 for (Instruction instruction: instructions) { 889 if (instruction.getOpcode() == Opcode.CONST_STRING) { 890 if (stringSection.getItemIndex( 891 (StringRef)((ReferenceInstruction)instruction).getReference()) >= 65536) { 892 needsFix = true; 893 break; 894 } 895 } 896 } 897 898 if (needsFix) { 899 MutableMethodImplementation mutableMethodImplementation = 900 classSection.makeMutableMethodImplementation(methodKey); 901 fixInstructions(mutableMethodImplementation); 902 903 instructions = mutableMethodImplementation.getInstructions(); 904 tryBlocks = mutableMethodImplementation.getTryBlocks(); 905 debugItems = mutableMethodImplementation.getDebugItems(); 906 } 907 } 908 909 int debugItemOffset = writeDebugItem(offsetWriter, debugWriter, 910 classSection.getParameterNames(methodKey), debugItems); 911 int codeItemOffset; 912 try { 913 codeItemOffset = writeCodeItem( 914 codeWriter, ehBuf, methodKey, tryBlocks, instructions, debugItemOffset); 915 } catch (RuntimeException ex) { 916 throw new ExceptionWithContext(ex, "Exception occurred while writing code_item for method %s", 917 methodSection.getMethodReference(methodKey)); 918 } 919 920 if (codeItemOffset != -1) { 921 codeOffsets.add(new CodeItemOffset<MethodKey>(methodKey, codeItemOffset)); 922 } 923 } 924 } 925 926 offsetWriter.align(); 927 codeSectionOffset = offsetWriter.getPosition(); 928 929 codeWriter.close(); 930 temp.writeTo(offsetWriter); 931 temp.close(); 932 933 for (CodeItemOffset<MethodKey> codeOffset: codeOffsets) { 934 classSection.setCodeItemOffset(codeOffset.method, codeSectionOffset + codeOffset.codeOffset); 935 } 936 } 937 938 private void fixInstructions(@Nonnull MutableMethodImplementation methodImplementation) { 939 List<? extends Instruction> instructions = methodImplementation.getInstructions(); 940 941 for (int i=0; i<instructions.size(); i++) { 942 Instruction instruction = instructions.get(i); 943 944 if (instruction.getOpcode() == Opcode.CONST_STRING) { 945 if (stringSection.getItemIndex( 946 (StringRef)((ReferenceInstruction)instruction).getReference()) >= 65536) { 947 methodImplementation.replaceInstruction(i, new BuilderInstruction31c(Opcode.CONST_STRING_JUMBO, 948 ((OneRegisterInstruction)instruction).getRegisterA(), 949 ((ReferenceInstruction)instruction).getReference())); 950 } 951 } 952 } 953 } 954 955 private int writeDebugItem(@Nonnull DexDataWriter writer, 956 @Nonnull DebugWriter<StringKey, TypeKey> debugWriter, 957 @Nullable Iterable<? extends StringKey> parameterNames, 958 @Nullable Iterable<? extends DebugItem> debugItems) throws IOException { 959 int parameterCount = 0; 960 int lastNamedParameterIndex = -1; 961 if (parameterNames != null) { 962 parameterCount = Iterables.size(parameterNames); 963 int index = 0; 964 for (StringKey parameterName: parameterNames) { 965 if (parameterName != null) { 966 lastNamedParameterIndex = index; 967 } 968 index++; 969 } 970 } 971 972 973 if (lastNamedParameterIndex == -1 && (debugItems == null || Iterables.isEmpty(debugItems))) { 974 return NO_OFFSET; 975 } 976 977 numDebugInfoItems++; 978 979 int debugItemOffset = writer.getPosition(); 980 int startingLineNumber = 0; 981 982 if (debugItems != null) { 983 for (org.jf.dexlib2.iface.debug.DebugItem debugItem: debugItems) { 984 if (debugItem instanceof LineNumber) { 985 startingLineNumber = ((LineNumber)debugItem).getLineNumber(); 986 break; 987 } 988 } 989 } 990 writer.writeUleb128(startingLineNumber); 991 992 writer.writeUleb128(parameterCount); 993 if (parameterNames != null) { 994 int index = 0; 995 for (StringKey parameterName: parameterNames) { 996 if (index == parameterCount) { 997 break; 998 } 999 index++; 1000 writer.writeUleb128(stringSection.getNullableItemIndex(parameterName) + 1); 1001 } 1002 } 1003 1004 if (debugItems != null) { 1005 debugWriter.reset(startingLineNumber); 1006 1007 for (DebugItem debugItem: debugItems) { 1008 classSection.writeDebugItem(debugWriter, debugItem); 1009 } 1010 } 1011 // write an END_SEQUENCE opcode, to end the debug item 1012 writer.write(0); 1013 1014 return debugItemOffset; 1015 } 1016 1017 private int writeCodeItem(@Nonnull DexDataWriter writer, 1018 @Nonnull ByteArrayOutputStream ehBuf, 1019 @Nonnull MethodKey methodKey, 1020 @Nonnull List<? extends TryBlock<? extends ExceptionHandler>> tryBlocks, 1021 @Nullable Iterable<? extends Instruction> instructions, 1022 int debugItemOffset) throws IOException { 1023 if (instructions == null && debugItemOffset == NO_OFFSET) { 1024 return -1; 1025 } 1026 1027 numCodeItemItems++; 1028 1029 writer.align(); 1030 1031 int codeItemOffset = writer.getPosition(); 1032 1033 writer.writeUshort(classSection.getRegisterCount(methodKey)); 1034 1035 boolean isStatic = AccessFlags.STATIC.isSet(classSection.getMethodAccessFlags(methodKey)); 1036 Collection<? extends TypeKey> parameters = typeListSection.getTypes( 1037 protoSection.getParameters(methodSection.getPrototype(methodKey))); 1038 1039 writer.writeUshort(MethodUtil.getParameterRegisterCount(parameters, isStatic)); 1040 1041 if (instructions != null) { 1042 tryBlocks = TryListBuilder.massageTryBlocks(tryBlocks); 1043 1044 int outParamCount = 0; 1045 int codeUnitCount = 0; 1046 for (Instruction instruction: instructions) { 1047 codeUnitCount += instruction.getCodeUnits(); 1048 if (instruction.getOpcode().referenceType == ReferenceType.METHOD) { 1049 ReferenceInstruction refInsn = (ReferenceInstruction)instruction; 1050 MethodReference methodRef = (MethodReference)refInsn.getReference(); 1051 Opcode opcode = instruction.getOpcode(); 1052 int paramCount; 1053 if (InstructionUtil.isInvokePolymorphic(opcode)) { 1054 paramCount = ((VariableRegisterInstruction)instruction).getRegisterCount(); 1055 } else { 1056 paramCount = MethodUtil.getParameterRegisterCount(methodRef, InstructionUtil.isInvokeStatic(opcode)); 1057 } 1058 if (paramCount > outParamCount) { 1059 outParamCount = paramCount; 1060 } 1061 } 1062 } 1063 1064 writer.writeUshort(outParamCount); 1065 writer.writeUshort(tryBlocks.size()); 1066 writer.writeInt(debugItemOffset); 1067 1068 InstructionWriter instructionWriter = 1069 InstructionWriter.makeInstructionWriter(opcodes, writer, stringSection, typeSection, fieldSection, 1070 methodSection, protoSection, methodHandleSection, callSiteSection); 1071 1072 writer.writeInt(codeUnitCount); 1073 int codeOffset = 0; 1074 for (Instruction instruction: instructions) { 1075 try { 1076 switch (instruction.getOpcode().format) { 1077 case Format10t: 1078 instructionWriter.write((Instruction10t)instruction); 1079 break; 1080 case Format10x: 1081 instructionWriter.write((Instruction10x)instruction); 1082 break; 1083 case Format11n: 1084 instructionWriter.write((Instruction11n)instruction); 1085 break; 1086 case Format11x: 1087 instructionWriter.write((Instruction11x)instruction); 1088 break; 1089 case Format12x: 1090 instructionWriter.write((Instruction12x)instruction); 1091 break; 1092 case Format20bc: 1093 instructionWriter.write((Instruction20bc)instruction); 1094 break; 1095 case Format20t: 1096 instructionWriter.write((Instruction20t)instruction); 1097 break; 1098 case Format21c: 1099 instructionWriter.write((Instruction21c)instruction); 1100 break; 1101 case Format21ih: 1102 instructionWriter.write((Instruction21ih)instruction); 1103 break; 1104 case Format21lh: 1105 instructionWriter.write((Instruction21lh)instruction); 1106 break; 1107 case Format21s: 1108 instructionWriter.write((Instruction21s)instruction); 1109 break; 1110 case Format21t: 1111 instructionWriter.write((Instruction21t)instruction); 1112 break; 1113 case Format22b: 1114 instructionWriter.write((Instruction22b)instruction); 1115 break; 1116 case Format22c: 1117 instructionWriter.write((Instruction22c)instruction); 1118 break; 1119 case Format22cs: 1120 instructionWriter.write((Instruction22cs)instruction); 1121 break; 1122 case Format22s: 1123 instructionWriter.write((Instruction22s)instruction); 1124 break; 1125 case Format22t: 1126 instructionWriter.write((Instruction22t)instruction); 1127 break; 1128 case Format22x: 1129 instructionWriter.write((Instruction22x)instruction); 1130 break; 1131 case Format23x: 1132 instructionWriter.write((Instruction23x)instruction); 1133 break; 1134 case Format30t: 1135 instructionWriter.write((Instruction30t)instruction); 1136 break; 1137 case Format31c: 1138 instructionWriter.write((Instruction31c)instruction); 1139 break; 1140 case Format31i: 1141 instructionWriter.write((Instruction31i)instruction); 1142 break; 1143 case Format31t: 1144 instructionWriter.write((Instruction31t)instruction); 1145 break; 1146 case Format32x: 1147 instructionWriter.write((Instruction32x)instruction); 1148 break; 1149 case Format35c: 1150 instructionWriter.write((Instruction35c)instruction); 1151 break; 1152 case Format35mi: 1153 instructionWriter.write((Instruction35mi)instruction); 1154 break; 1155 case Format35ms: 1156 instructionWriter.write((Instruction35ms)instruction); 1157 break; 1158 case Format3rc: 1159 instructionWriter.write((Instruction3rc)instruction); 1160 break; 1161 case Format3rmi: 1162 instructionWriter.write((Instruction3rmi)instruction); 1163 break; 1164 case Format3rms: 1165 instructionWriter.write((Instruction3rms)instruction); 1166 break; 1167 case Format45cc: 1168 instructionWriter.write((Instruction45cc)instruction); 1169 break; 1170 case Format4rcc: 1171 instructionWriter.write((Instruction4rcc)instruction); 1172 break; 1173 case Format51l: 1174 instructionWriter.write((Instruction51l)instruction); 1175 break; 1176 case ArrayPayload: 1177 instructionWriter.write((ArrayPayload)instruction); 1178 break; 1179 case PackedSwitchPayload: 1180 instructionWriter.write((PackedSwitchPayload)instruction); 1181 break; 1182 case SparseSwitchPayload: 1183 instructionWriter.write((SparseSwitchPayload)instruction); 1184 break; 1185 default: 1186 throw new ExceptionWithContext("Unsupported instruction format: %s", 1187 instruction.getOpcode().format); 1188 } 1189 } catch (RuntimeException ex) { 1190 throw new ExceptionWithContext(ex, "Error while writing instruction at code offset 0x%x", codeOffset); 1191 } 1192 codeOffset += instruction.getCodeUnits(); 1193 } 1194 1195 if (tryBlocks.size() > 0) { 1196 writer.align(); 1197 1198 // filter out unique lists of exception handlers 1199 Map<List<? extends ExceptionHandler>, Integer> exceptionHandlerOffsetMap = Maps.newHashMap(); 1200 for (TryBlock<? extends ExceptionHandler> tryBlock: tryBlocks) { 1201 exceptionHandlerOffsetMap.put(tryBlock.getExceptionHandlers(), 0); 1202 } 1203 DexDataWriter.writeUleb128(ehBuf, exceptionHandlerOffsetMap.size()); 1204 1205 for (TryBlock<? extends ExceptionHandler> tryBlock: tryBlocks) { 1206 int startAddress = tryBlock.getStartCodeAddress(); 1207 int endAddress = startAddress + tryBlock.getCodeUnitCount(); 1208 1209 int tbCodeUnitCount = endAddress - startAddress; 1210 1211 writer.writeInt(startAddress); 1212 writer.writeUshort(tbCodeUnitCount); 1213 1214 if (tryBlock.getExceptionHandlers().size() == 0) { 1215 throw new ExceptionWithContext("No exception handlers for the try block!"); 1216 } 1217 1218 Integer offset = exceptionHandlerOffsetMap.get(tryBlock.getExceptionHandlers()); 1219 if (offset != 0) { 1220 // exception handler has already been written out, just use it 1221 writer.writeUshort(offset); 1222 } else { 1223 // if offset has not been set yet, we are about to write out a new exception handler 1224 offset = ehBuf.size(); 1225 writer.writeUshort(offset); 1226 exceptionHandlerOffsetMap.put(tryBlock.getExceptionHandlers(), offset); 1227 1228 // check if the last exception handler is a catch-all and adjust the size accordingly 1229 int ehSize = tryBlock.getExceptionHandlers().size(); 1230 ExceptionHandler ehLast = tryBlock.getExceptionHandlers().get(ehSize-1); 1231 if (ehLast.getExceptionType() == null) { 1232 ehSize = ehSize * (-1) + 1; 1233 } 1234 1235 // now let's layout the exception handlers, assuming that catch-all is always last 1236 DexDataWriter.writeSleb128(ehBuf, ehSize); 1237 for (ExceptionHandler eh : tryBlock.getExceptionHandlers()) { 1238 TypeKey exceptionTypeKey = classSection.getExceptionType(eh); 1239 1240 int codeAddress = eh.getHandlerCodeAddress(); 1241 1242 if (exceptionTypeKey != null) { 1243 //regular exception handling 1244 DexDataWriter.writeUleb128(ehBuf, typeSection.getItemIndex(exceptionTypeKey)); 1245 DexDataWriter.writeUleb128(ehBuf, codeAddress); 1246 } else { 1247 //catch-all 1248 DexDataWriter.writeUleb128(ehBuf, codeAddress); 1249 } 1250 } 1251 } 1252 } 1253 1254 if (ehBuf.size() > 0) { 1255 ehBuf.writeTo(writer); 1256 ehBuf.reset(); 1257 } 1258 } 1259 } else { 1260 // no instructions, all we have is the debug item offset 1261 writer.writeUshort(0); 1262 writer.writeUshort(0); 1263 writer.writeInt(debugItemOffset); 1264 writer.writeInt(0); 1265 } 1266 1267 return codeItemOffset; 1268 } 1269 1270 private int calcNumItems() { 1271 int numItems = 0; 1272 1273 // header item 1274 numItems++; 1275 1276 if (stringSection.getItems().size() > 0) { 1277 numItems += 2; // index and data 1278 } 1279 if (typeSection.getItems().size() > 0) { 1280 numItems++; 1281 } 1282 if (protoSection.getItems().size() > 0) { 1283 numItems++; 1284 } 1285 if (fieldSection.getItems().size() > 0) { 1286 numItems++; 1287 } 1288 if (methodSection.getItems().size() > 0) { 1289 numItems++; 1290 } 1291 if (callSiteSection.getItems().size() > 0) { 1292 numItems++; 1293 } 1294 if (methodHandleSection.getItems().size() > 0) { 1295 numItems++; 1296 } 1297 if (typeListSection.getItems().size() > 0) { 1298 numItems++; 1299 } 1300 if (encodedArraySection.getItems().size() > 0) { 1301 numItems++; 1302 } 1303 if (annotationSection.getItems().size() > 0) { 1304 numItems++; 1305 } 1306 if (annotationSetSection.getItems().size() > 0 || shouldCreateEmptyAnnotationSet()) { 1307 numItems++; 1308 } 1309 if (numAnnotationSetRefItems > 0) { 1310 numItems++; 1311 } 1312 if (numAnnotationDirectoryItems > 0) { 1313 numItems++; 1314 } 1315 if (numDebugInfoItems > 0) { 1316 numItems++; 1317 } 1318 if (numCodeItemItems > 0) { 1319 numItems++; 1320 } 1321 if (classSection.getItems().size() > 0) { 1322 numItems++; 1323 } 1324 if (numClassDataItems > 0) { 1325 numItems++; 1326 } 1327 // map item itself 1328 numItems++; 1329 1330 return numItems; 1331 } 1332 1333 private void writeMapItem(@Nonnull DexDataWriter writer) throws IOException{ 1334 writer.align(); 1335 mapSectionOffset = writer.getPosition(); 1336 int numItems = calcNumItems(); 1337 1338 writer.writeInt(numItems); 1339 1340 // index section 1341 writeMapItem(writer, ItemType.HEADER_ITEM, 1, 0); 1342 writeMapItem(writer, ItemType.STRING_ID_ITEM, stringSection.getItems().size(), stringIndexSectionOffset); 1343 writeMapItem(writer, ItemType.TYPE_ID_ITEM, typeSection.getItems().size(), typeSectionOffset); 1344 writeMapItem(writer, ItemType.PROTO_ID_ITEM, protoSection.getItems().size(), protoSectionOffset); 1345 writeMapItem(writer, ItemType.FIELD_ID_ITEM, fieldSection.getItems().size(), fieldSectionOffset); 1346 writeMapItem(writer, ItemType.METHOD_ID_ITEM, methodSection.getItems().size(), methodSectionOffset); 1347 writeMapItem(writer, ItemType.CLASS_DEF_ITEM, classSection.getItems().size(), classIndexSectionOffset); 1348 writeMapItem(writer, ItemType.CALL_SITE_ID_ITEM, callSiteSection.getItems().size(), callSiteSectionOffset); 1349 writeMapItem(writer, ItemType.METHOD_HANDLE_ITEM, methodHandleSection.getItems().size(), 1350 methodHandleSectionOffset); 1351 1352 // data section 1353 writeMapItem(writer, ItemType.STRING_DATA_ITEM, stringSection.getItems().size(), stringDataSectionOffset); 1354 writeMapItem(writer, ItemType.TYPE_LIST, typeListSection.getItems().size(), typeListSectionOffset); 1355 writeMapItem(writer, ItemType.ENCODED_ARRAY_ITEM, encodedArraySection.getItems().size(), 1356 encodedArraySectionOffset); 1357 writeMapItem(writer, ItemType.ANNOTATION_ITEM, annotationSection.getItems().size(), annotationSectionOffset); 1358 writeMapItem(writer, ItemType.ANNOTATION_SET_ITEM, 1359 annotationSetSection.getItems().size() + (shouldCreateEmptyAnnotationSet() ? 1 : 0), 1360 annotationSetSectionOffset); 1361 writeMapItem(writer, ItemType.ANNOTATION_SET_REF_LIST, numAnnotationSetRefItems, annotationSetRefSectionOffset); 1362 writeMapItem(writer, ItemType.ANNOTATION_DIRECTORY_ITEM, numAnnotationDirectoryItems, 1363 annotationDirectorySectionOffset); 1364 writeMapItem(writer, ItemType.DEBUG_INFO_ITEM, numDebugInfoItems, debugSectionOffset); 1365 writeMapItem(writer, ItemType.CODE_ITEM, numCodeItemItems, codeSectionOffset); 1366 writeMapItem(writer, ItemType.CLASS_DATA_ITEM, numClassDataItems, classDataSectionOffset); 1367 writeMapItem(writer, ItemType.MAP_LIST, 1, mapSectionOffset); 1368 } 1369 1370 private void writeMapItem(@Nonnull DexDataWriter writer, int type, int size, int offset) throws IOException { 1371 if (size > 0) { 1372 writer.writeUshort(type); 1373 writer.writeUshort(0); 1374 writer.writeInt(size); 1375 writer.writeInt(offset); 1376 } 1377 } 1378 1379 private void writeHeader(@Nonnull DexDataWriter writer, int dataOffset, int fileSize) throws IOException { 1380 // Write the appropriate header. 1381 writer.write(HeaderItem.getMagicForApi(opcodes.api)); 1382 1383 // checksum placeholder 1384 writer.writeInt(0); 1385 1386 // signature placeholder 1387 writer.write(new byte[20]); 1388 1389 writer.writeInt(fileSize); 1390 writer.writeInt(HeaderItem.ITEM_SIZE); 1391 writer.writeInt(HeaderItem.LITTLE_ENDIAN_TAG); 1392 1393 // link 1394 writer.writeInt(0); 1395 writer.writeInt(0); 1396 1397 // map 1398 writer.writeInt(mapSectionOffset); 1399 1400 // index sections 1401 1402 writeSectionInfo(writer, stringSection.getItems().size(), stringIndexSectionOffset); 1403 writeSectionInfo(writer, typeSection.getItems().size(), typeSectionOffset); 1404 writeSectionInfo(writer, protoSection.getItems().size(), protoSectionOffset); 1405 writeSectionInfo(writer, fieldSection.getItems().size(), fieldSectionOffset); 1406 writeSectionInfo(writer, methodSection.getItems().size(), methodSectionOffset); 1407 writeSectionInfo(writer, classSection.getItems().size(), classIndexSectionOffset); 1408 1409 // data section 1410 writer.writeInt(fileSize - dataOffset); 1411 writer.writeInt(dataOffset); 1412 } 1413 1414 private void writeSectionInfo(DexDataWriter writer, int numItems, int offset) throws IOException { 1415 writer.writeInt(numItems); 1416 if (numItems > 0) { 1417 writer.writeInt(offset); 1418 } else { 1419 writer.writeInt(0); 1420 } 1421 } 1422 1423 private boolean shouldCreateEmptyAnnotationSet() { 1424 // Workaround for a crash in Dalvik VM before Jelly Bean MR1 (4.2) 1425 // which is triggered by NO_OFFSET in parameter annotation list. 1426 // (https://code.google.com/p/android/issues/detail?id=35304) 1427 return (opcodes.api < 17); 1428 } 1429 1430 public abstract class SectionProvider { 1431 @Nonnull public abstract StringSectionType getStringSection(); 1432 @Nonnull public abstract TypeSectionType getTypeSection(); 1433 @Nonnull public abstract ProtoSectionType getProtoSection(); 1434 @Nonnull public abstract FieldSectionType getFieldSection(); 1435 @Nonnull public abstract MethodSectionType getMethodSection(); 1436 @Nonnull public abstract ClassSectionType getClassSection(); 1437 @Nonnull public abstract CallSiteSectionType getCallSiteSection(); 1438 @Nonnull public abstract MethodHandleSectionType getMethodHandleSection(); 1439 @Nonnull public abstract TypeListSectionType getTypeListSection(); 1440 @Nonnull public abstract AnnotationSectionType getAnnotationSection(); 1441 @Nonnull public abstract AnnotationSetSectionType getAnnotationSetSection(); 1442 @Nonnull public abstract EncodedArraySectionType getEncodedArraySection(); 1443 } 1444 } 1445