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