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