1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.dex; 18 19 import com.android.dex.Code.CatchHandler; 20 import com.android.dex.Code.Try; 21 import com.android.dex.util.ByteInput; 22 import com.android.dex.util.ByteOutput; 23 import com.android.dex.util.FileUtils; 24 25 import java.io.ByteArrayOutputStream; 26 import java.io.File; 27 import java.io.FileInputStream; 28 import java.io.FileOutputStream; 29 import java.io.IOException; 30 import java.io.InputStream; 31 import java.io.OutputStream; 32 import java.io.UTFDataFormatException; 33 import java.nio.ByteBuffer; 34 import java.nio.ByteOrder; 35 import java.security.MessageDigest; 36 import java.security.NoSuchAlgorithmException; 37 import java.util.AbstractList; 38 import java.util.Collections; 39 import java.util.Iterator; 40 import java.util.List; 41 import java.util.NoSuchElementException; 42 import java.util.RandomAccess; 43 import java.util.zip.Adler32; 44 import java.util.zip.ZipEntry; 45 import java.util.zip.ZipFile; 46 47 /** 48 * The bytes of a dex file in memory for reading and writing. All int offsets 49 * are unsigned. 50 */ 51 public final class Dex { 52 private static final int CHECKSUM_OFFSET = 8; 53 private static final int CHECKSUM_SIZE = 4; 54 private static final int SIGNATURE_OFFSET = CHECKSUM_OFFSET + CHECKSUM_SIZE; 55 private static final int SIGNATURE_SIZE = 20; 56 // Provided as a convenience to avoid a memory allocation to benefit Dalvik. 57 // Note: libcore.util.EmptyArray cannot be accessed when this code isn't run on Dalvik. 58 static final short[] EMPTY_SHORT_ARRAY = new short[0]; 59 60 private ByteBuffer data; 61 private final TableOfContents tableOfContents = new TableOfContents(); 62 private int nextSectionStart = 0; 63 private final StringTable strings = new StringTable(); 64 private final TypeIndexToDescriptorIndexTable typeIds = new TypeIndexToDescriptorIndexTable(); 65 private final TypeIndexToDescriptorTable typeNames = new TypeIndexToDescriptorTable(); 66 private final ProtoIdTable protoIds = new ProtoIdTable(); 67 private final FieldIdTable fieldIds = new FieldIdTable(); 68 private final MethodIdTable methodIds = new MethodIdTable(); 69 70 /** 71 * Creates a new dex that reads from {@code data}. It is an error to modify 72 * {@code data} after using it to create a dex buffer. 73 */ 74 public Dex(byte[] data) throws IOException { 75 this(ByteBuffer.wrap(data)); 76 } 77 78 private Dex(ByteBuffer data) throws IOException { 79 this.data = data; 80 this.data.order(ByteOrder.LITTLE_ENDIAN); 81 this.tableOfContents.readFrom(this); 82 } 83 84 /** 85 * Creates a new empty dex of the specified size. 86 */ 87 public Dex(int byteCount) throws IOException { 88 this.data = ByteBuffer.wrap(new byte[byteCount]); 89 this.data.order(ByteOrder.LITTLE_ENDIAN); 90 } 91 92 /** 93 * Creates a new dex buffer of the dex in {@code in}, and closes {@code in}. 94 */ 95 public Dex(InputStream in) throws IOException { 96 loadFrom(in); 97 } 98 99 /** 100 * Creates a new dex buffer from the dex file {@code file}. 101 */ 102 public Dex(File file) throws IOException { 103 if (FileUtils.hasArchiveSuffix(file.getName())) { 104 ZipFile zipFile = new ZipFile(file); 105 ZipEntry entry = zipFile.getEntry(DexFormat.DEX_IN_JAR_NAME); 106 if (entry != null) { 107 loadFrom(zipFile.getInputStream(entry)); 108 zipFile.close(); 109 } else { 110 throw new DexException("Expected " + DexFormat.DEX_IN_JAR_NAME + " in " + file); 111 } 112 } else if (file.getName().endsWith(".dex")) { 113 loadFrom(new FileInputStream(file)); 114 } else { 115 throw new DexException("unknown output extension: " + file); 116 } 117 } 118 119 /** 120 * Creates a new dex from the contents of {@code bytes}. This API supports 121 * both {@code .dex} and {@code .odex} input. Calling this constructor 122 * transfers ownership of {@code bytes} to the returned Dex: it is an error 123 * to access the buffer after calling this method. 124 */ 125 public static Dex create(ByteBuffer data) throws IOException { 126 data.order(ByteOrder.LITTLE_ENDIAN); 127 128 // if it's an .odex file, set position and limit to the .dex section 129 if (data.get(0) == 'd' 130 && data.get(1) == 'e' 131 && data.get(2) == 'y' 132 && data.get(3) == '\n') { 133 data.position(8); 134 int offset = data.getInt(); 135 int length = data.getInt(); 136 data.position(offset); 137 data.limit(offset + length); 138 data = data.slice(); 139 } 140 141 return new Dex(data); 142 } 143 144 private void loadFrom(InputStream in) throws IOException { 145 ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); 146 byte[] buffer = new byte[8192]; 147 148 int count; 149 while ((count = in.read(buffer)) != -1) { 150 bytesOut.write(buffer, 0, count); 151 } 152 in.close(); 153 154 this.data = ByteBuffer.wrap(bytesOut.toByteArray()); 155 this.data.order(ByteOrder.LITTLE_ENDIAN); 156 this.tableOfContents.readFrom(this); 157 } 158 159 private static void checkBounds(int index, int length) { 160 if (index < 0 || index >= length) { 161 throw new IndexOutOfBoundsException("index:" + index + ", length=" + length); 162 } 163 } 164 165 public void writeTo(OutputStream out) throws IOException { 166 byte[] buffer = new byte[8192]; 167 ByteBuffer data = this.data.duplicate(); // positioned ByteBuffers aren't thread safe 168 data.clear(); 169 while (data.hasRemaining()) { 170 int count = Math.min(buffer.length, data.remaining()); 171 data.get(buffer, 0, count); 172 out.write(buffer, 0, count); 173 } 174 } 175 176 public void writeTo(File dexOut) throws IOException { 177 OutputStream out = new FileOutputStream(dexOut); 178 writeTo(out); 179 out.close(); 180 } 181 182 public TableOfContents getTableOfContents() { 183 return tableOfContents; 184 } 185 186 public Section open(int position) { 187 if (position < 0 || position >= data.capacity()) { 188 throw new IllegalArgumentException("position=" + position 189 + " length=" + data.capacity()); 190 } 191 ByteBuffer sectionData = data.duplicate(); 192 sectionData.order(ByteOrder.LITTLE_ENDIAN); // necessary? 193 sectionData.position(position); 194 sectionData.limit(data.capacity()); 195 return new Section("section", sectionData); 196 } 197 198 public Section appendSection(int maxByteCount, String name) { 199 if ((maxByteCount & 3) != 0) { 200 throw new IllegalStateException("Not four byte aligned!"); 201 } 202 int limit = nextSectionStart + maxByteCount; 203 ByteBuffer sectionData = data.duplicate(); 204 sectionData.order(ByteOrder.LITTLE_ENDIAN); // necessary? 205 sectionData.position(nextSectionStart); 206 sectionData.limit(limit); 207 Section result = new Section(name, sectionData); 208 nextSectionStart = limit; 209 return result; 210 } 211 212 public int getLength() { 213 return data.capacity(); 214 } 215 216 public int getNextSectionStart() { 217 return nextSectionStart; 218 } 219 220 /** 221 * Returns a copy of the the bytes of this dex. 222 */ 223 public byte[] getBytes() { 224 ByteBuffer data = this.data.duplicate(); // positioned ByteBuffers aren't thread safe 225 byte[] result = new byte[data.capacity()]; 226 data.position(0); 227 data.get(result); 228 return result; 229 } 230 231 public List<String> strings() { 232 return strings; 233 } 234 235 public List<Integer> typeIds() { 236 return typeIds; 237 } 238 239 public List<String> typeNames() { 240 return typeNames; 241 } 242 243 public List<ProtoId> protoIds() { 244 return protoIds; 245 } 246 247 public List<FieldId> fieldIds() { 248 return fieldIds; 249 } 250 251 public List<MethodId> methodIds() { 252 return methodIds; 253 } 254 255 public Iterable<ClassDef> classDefs() { 256 return new ClassDefIterable(); 257 } 258 259 public TypeList readTypeList(int offset) { 260 if (offset == 0) { 261 return TypeList.EMPTY; 262 } 263 return open(offset).readTypeList(); 264 } 265 266 public ClassData readClassData(ClassDef classDef) { 267 int offset = classDef.getClassDataOffset(); 268 if (offset == 0) { 269 throw new IllegalArgumentException("offset == 0"); 270 } 271 return open(offset).readClassData(); 272 } 273 274 public Code readCode(ClassData.Method method) { 275 int offset = method.getCodeOffset(); 276 if (offset == 0) { 277 throw new IllegalArgumentException("offset == 0"); 278 } 279 return open(offset).readCode(); 280 } 281 282 /** 283 * Returns the signature of all but the first 32 bytes of this dex. The 284 * first 32 bytes of dex files are not specified to be included in the 285 * signature. 286 */ 287 public byte[] computeSignature() throws IOException { 288 MessageDigest digest; 289 try { 290 digest = MessageDigest.getInstance("SHA-1"); 291 } catch (NoSuchAlgorithmException e) { 292 throw new AssertionError(); 293 } 294 byte[] buffer = new byte[8192]; 295 ByteBuffer data = this.data.duplicate(); // positioned ByteBuffers aren't thread safe 296 data.limit(data.capacity()); 297 data.position(SIGNATURE_OFFSET + SIGNATURE_SIZE); 298 while (data.hasRemaining()) { 299 int count = Math.min(buffer.length, data.remaining()); 300 data.get(buffer, 0, count); 301 digest.update(buffer, 0, count); 302 } 303 return digest.digest(); 304 } 305 306 /** 307 * Returns the checksum of all but the first 12 bytes of {@code dex}. 308 */ 309 public int computeChecksum() throws IOException { 310 Adler32 adler32 = new Adler32(); 311 byte[] buffer = new byte[8192]; 312 ByteBuffer data = this.data.duplicate(); // positioned ByteBuffers aren't thread safe 313 data.limit(data.capacity()); 314 data.position(CHECKSUM_OFFSET + CHECKSUM_SIZE); 315 while (data.hasRemaining()) { 316 int count = Math.min(buffer.length, data.remaining()); 317 data.get(buffer, 0, count); 318 adler32.update(buffer, 0, count); 319 } 320 return (int) adler32.getValue(); 321 } 322 323 /** 324 * Generates the signature and checksum of the dex file {@code out} and 325 * writes them to the file. 326 */ 327 public void writeHashes() throws IOException { 328 open(SIGNATURE_OFFSET).write(computeSignature()); 329 open(CHECKSUM_OFFSET).writeInt(computeChecksum()); 330 } 331 332 /** 333 * Look up a field id name index from a field index. Cheaper than: 334 * {@code fieldIds().get(fieldDexIndex).getNameIndex();} 335 */ 336 public int nameIndexFromFieldIndex(int fieldIndex) { 337 checkBounds(fieldIndex, tableOfContents.fieldIds.size); 338 int position = tableOfContents.fieldIds.off + (SizeOf.MEMBER_ID_ITEM * fieldIndex); 339 position += SizeOf.USHORT; // declaringClassIndex 340 position += SizeOf.USHORT; // typeIndex 341 return data.getInt(position); // nameIndex 342 } 343 344 public int findStringIndex(String s) { 345 return Collections.binarySearch(strings, s); 346 } 347 348 public int findTypeIndex(String descriptor) { 349 return Collections.binarySearch(typeNames, descriptor); 350 } 351 352 public int findFieldIndex(FieldId fieldId) { 353 return Collections.binarySearch(fieldIds, fieldId); 354 } 355 356 public int findMethodIndex(MethodId methodId) { 357 return Collections.binarySearch(methodIds, methodId); 358 } 359 360 public int findClassDefIndexFromTypeIndex(int typeIndex) { 361 checkBounds(typeIndex, tableOfContents.typeIds.size); 362 if (!tableOfContents.classDefs.exists()) { 363 return -1; 364 } 365 for (int i = 0; i < tableOfContents.classDefs.size; i++) { 366 if (typeIndexFromClassDefIndex(i) == typeIndex) { 367 return i; 368 } 369 } 370 return -1; 371 } 372 373 /** 374 * Look up a field id type index from a field index. Cheaper than: 375 * {@code fieldIds().get(fieldDexIndex).getTypeIndex();} 376 */ 377 public int typeIndexFromFieldIndex(int fieldIndex) { 378 checkBounds(fieldIndex, tableOfContents.fieldIds.size); 379 int position = tableOfContents.fieldIds.off + (SizeOf.MEMBER_ID_ITEM * fieldIndex); 380 position += SizeOf.USHORT; // declaringClassIndex 381 return data.getShort(position) & 0xFFFF; // typeIndex 382 } 383 384 /** 385 * Look up a method id declaring class index from a method index. Cheaper than: 386 * {@code methodIds().get(methodIndex).getDeclaringClassIndex();} 387 */ 388 public int declaringClassIndexFromMethodIndex(int methodIndex) { 389 checkBounds(methodIndex, tableOfContents.methodIds.size); 390 int position = tableOfContents.methodIds.off + (SizeOf.MEMBER_ID_ITEM * methodIndex); 391 return data.getShort(position) & 0xFFFF; // declaringClassIndex 392 } 393 394 /** 395 * Look up a method id name index from a method index. Cheaper than: 396 * {@code methodIds().get(methodIndex).getNameIndex();} 397 */ 398 public int nameIndexFromMethodIndex(int methodIndex) { 399 checkBounds(methodIndex, tableOfContents.methodIds.size); 400 int position = tableOfContents.methodIds.off + (SizeOf.MEMBER_ID_ITEM * methodIndex); 401 position += SizeOf.USHORT; // declaringClassIndex 402 position += SizeOf.USHORT; // protoIndex 403 return data.getInt(position); // nameIndex 404 } 405 406 /** 407 * Look up a parameter type ids from a method index. Cheaper than: 408 * {@code readTypeList(protoIds.get(methodIds().get(methodDexIndex).getProtoIndex()).getParametersOffset()).getTypes();} 409 */ 410 public short[] parameterTypeIndicesFromMethodIndex(int methodIndex) { 411 checkBounds(methodIndex, tableOfContents.methodIds.size); 412 int position = tableOfContents.methodIds.off + (SizeOf.MEMBER_ID_ITEM * methodIndex); 413 position += SizeOf.USHORT; // declaringClassIndex 414 int protoIndex = data.getShort(position) & 0xFFFF; 415 checkBounds(protoIndex, tableOfContents.protoIds.size); 416 position = tableOfContents.protoIds.off + (SizeOf.PROTO_ID_ITEM * protoIndex); 417 position += SizeOf.UINT; // shortyIndex 418 position += SizeOf.UINT; // returnTypeIndex 419 int parametersOffset = data.getInt(position); 420 if (parametersOffset == 0) { 421 return EMPTY_SHORT_ARRAY; 422 } 423 position = parametersOffset; 424 int size = data.getInt(position); 425 if (size <= 0) { 426 throw new AssertionError("Unexpected parameter type list size: " + size); 427 } 428 position += SizeOf.UINT; 429 short[] types = new short[size]; 430 for (int i = 0; i < size; i++) { 431 types[i] = data.getShort(position); 432 position += SizeOf.USHORT; 433 } 434 return types; 435 } 436 437 /** 438 * Look up a method id return type index from a method index. Cheaper than: 439 * {@code protoIds().get(methodIds().get(methodDexIndex).getProtoIndex()).getReturnTypeIndex();} 440 */ 441 public int returnTypeIndexFromMethodIndex(int methodIndex) { 442 checkBounds(methodIndex, tableOfContents.methodIds.size); 443 int position = tableOfContents.methodIds.off + (SizeOf.MEMBER_ID_ITEM * methodIndex); 444 position += SizeOf.USHORT; // declaringClassIndex 445 int protoIndex = data.getShort(position) & 0xFFFF; 446 checkBounds(protoIndex, tableOfContents.protoIds.size); 447 position = tableOfContents.protoIds.off + (SizeOf.PROTO_ID_ITEM * protoIndex); 448 position += SizeOf.UINT; // shortyIndex 449 return data.getInt(position); // returnTypeIndex 450 } 451 452 /** 453 * Look up a descriptor index from a type index. Cheaper than: 454 * {@code open(tableOfContents.typeIds.off + (index * SizeOf.TYPE_ID_ITEM)).readInt();} 455 */ 456 public int descriptorIndexFromTypeIndex(int typeIndex) { 457 checkBounds(typeIndex, tableOfContents.typeIds.size); 458 int position = tableOfContents.typeIds.off + (SizeOf.TYPE_ID_ITEM * typeIndex); 459 return data.getInt(position); 460 } 461 462 /** 463 * Look up a type index index from a class def index. 464 */ 465 public int typeIndexFromClassDefIndex(int classDefIndex) { 466 checkBounds(classDefIndex, tableOfContents.classDefs.size); 467 int position = tableOfContents.classDefs.off + (SizeOf.CLASS_DEF_ITEM * classDefIndex); 468 return data.getInt(position); 469 } 470 471 /** 472 * Look up a type index index from a class def index. 473 */ 474 public int annotationDirectoryOffsetFromClassDefIndex(int classDefIndex) { 475 checkBounds(classDefIndex, tableOfContents.classDefs.size); 476 int position = tableOfContents.classDefs.off + (SizeOf.CLASS_DEF_ITEM * classDefIndex); 477 position += SizeOf.UINT; // type 478 position += SizeOf.UINT; // accessFlags 479 position += SizeOf.UINT; // superType 480 position += SizeOf.UINT; // interfacesOffset 481 position += SizeOf.UINT; // sourceFileIndex 482 return data.getInt(position); 483 } 484 485 /** 486 * Look up interface types indices from a return type index from a method index. Cheaper than: 487 * {@code ...getClassDef(classDefIndex).getInterfaces();} 488 */ 489 public short[] interfaceTypeIndicesFromClassDefIndex(int classDefIndex) { 490 checkBounds(classDefIndex, tableOfContents.classDefs.size); 491 int position = tableOfContents.classDefs.off + (SizeOf.CLASS_DEF_ITEM * classDefIndex); 492 position += SizeOf.UINT; // type 493 position += SizeOf.UINT; // accessFlags 494 position += SizeOf.UINT; // superType 495 int interfacesOffset = data.getInt(position); 496 if (interfacesOffset == 0) { 497 return EMPTY_SHORT_ARRAY; 498 } 499 position = interfacesOffset; 500 int size = data.getInt(position); 501 if (size <= 0) { 502 throw new AssertionError("Unexpected interfaces list size: " + size); 503 } 504 position += SizeOf.UINT; 505 short[] types = new short[size]; 506 for (int i = 0; i < size; i++) { 507 types[i] = data.getShort(position); 508 position += SizeOf.USHORT; 509 } 510 return types; 511 } 512 513 public final class Section implements ByteInput, ByteOutput { 514 private final String name; 515 private final ByteBuffer data; 516 private final int initialPosition; 517 518 private Section(String name, ByteBuffer data) { 519 this.name = name; 520 this.data = data; 521 this.initialPosition = data.position(); 522 } 523 524 public int getPosition() { 525 return data.position(); 526 } 527 528 public int readInt() { 529 return data.getInt(); 530 } 531 532 public short readShort() { 533 return data.getShort(); 534 } 535 536 public int readUnsignedShort() { 537 return readShort() & 0xffff; 538 } 539 540 public byte readByte() { 541 return data.get(); 542 } 543 544 public byte[] readByteArray(int length) { 545 byte[] result = new byte[length]; 546 data.get(result); 547 return result; 548 } 549 550 public short[] readShortArray(int length) { 551 if (length == 0) { 552 return EMPTY_SHORT_ARRAY; 553 } 554 short[] result = new short[length]; 555 for (int i = 0; i < length; i++) { 556 result[i] = readShort(); 557 } 558 return result; 559 } 560 561 public int readUleb128() { 562 return Leb128.readUnsignedLeb128(this); 563 } 564 565 public int readUleb128p1() { 566 return Leb128.readUnsignedLeb128(this) - 1; 567 } 568 569 public int readSleb128() { 570 return Leb128.readSignedLeb128(this); 571 } 572 573 public void writeUleb128p1(int i) { 574 writeUleb128(i + 1); 575 } 576 577 public TypeList readTypeList() { 578 int size = readInt(); 579 short[] types = readShortArray(size); 580 alignToFourBytes(); 581 return new TypeList(Dex.this, types); 582 } 583 584 public String readString() { 585 int offset = readInt(); 586 int savedPosition = data.position(); 587 int savedLimit = data.limit(); 588 data.position(offset); 589 data.limit(data.capacity()); 590 try { 591 int expectedLength = readUleb128(); 592 String result = Mutf8.decode(this, new char[expectedLength]); 593 if (result.length() != expectedLength) { 594 throw new DexException("Declared length " + expectedLength 595 + " doesn't match decoded length of " + result.length()); 596 } 597 return result; 598 } catch (UTFDataFormatException e) { 599 throw new DexException(e); 600 } finally { 601 data.position(savedPosition); 602 data.limit(savedLimit); 603 } 604 } 605 606 public FieldId readFieldId() { 607 int declaringClassIndex = readUnsignedShort(); 608 int typeIndex = readUnsignedShort(); 609 int nameIndex = readInt(); 610 return new FieldId(Dex.this, declaringClassIndex, typeIndex, nameIndex); 611 } 612 613 public MethodId readMethodId() { 614 int declaringClassIndex = readUnsignedShort(); 615 int protoIndex = readUnsignedShort(); 616 int nameIndex = readInt(); 617 return new MethodId(Dex.this, declaringClassIndex, protoIndex, nameIndex); 618 } 619 620 public ProtoId readProtoId() { 621 int shortyIndex = readInt(); 622 int returnTypeIndex = readInt(); 623 int parametersOffset = readInt(); 624 return new ProtoId(Dex.this, shortyIndex, returnTypeIndex, parametersOffset); 625 } 626 627 public ClassDef readClassDef() { 628 int offset = getPosition(); 629 int type = readInt(); 630 int accessFlags = readInt(); 631 int supertype = readInt(); 632 int interfacesOffset = readInt(); 633 int sourceFileIndex = readInt(); 634 int annotationsOffset = readInt(); 635 int classDataOffset = readInt(); 636 int staticValuesOffset = readInt(); 637 return new ClassDef(Dex.this, offset, type, accessFlags, supertype, 638 interfacesOffset, sourceFileIndex, annotationsOffset, classDataOffset, 639 staticValuesOffset); 640 } 641 642 private Code readCode() { 643 int registersSize = readUnsignedShort(); 644 int insSize = readUnsignedShort(); 645 int outsSize = readUnsignedShort(); 646 int triesSize = readUnsignedShort(); 647 int debugInfoOffset = readInt(); 648 int instructionsSize = readInt(); 649 short[] instructions = readShortArray(instructionsSize); 650 Try[] tries; 651 CatchHandler[] catchHandlers; 652 if (triesSize > 0) { 653 if (instructions.length % 2 == 1) { 654 readShort(); // padding 655 } 656 657 /* 658 * We can't read the tries until we've read the catch handlers. 659 * Unfortunately they're in the opposite order in the dex file 660 * so we need to read them out-of-order. 661 */ 662 Section triesSection = open(data.position()); 663 skip(triesSize * SizeOf.TRY_ITEM); 664 catchHandlers = readCatchHandlers(); 665 tries = triesSection.readTries(triesSize, catchHandlers); 666 } else { 667 tries = new Try[0]; 668 catchHandlers = new CatchHandler[0]; 669 } 670 return new Code(registersSize, insSize, outsSize, debugInfoOffset, instructions, 671 tries, catchHandlers); 672 } 673 674 private CatchHandler[] readCatchHandlers() { 675 int baseOffset = data.position(); 676 int catchHandlersSize = readUleb128(); 677 CatchHandler[] result = new CatchHandler[catchHandlersSize]; 678 for (int i = 0; i < catchHandlersSize; i++) { 679 int offset = data.position() - baseOffset; 680 result[i] = readCatchHandler(offset); 681 } 682 return result; 683 } 684 685 private Try[] readTries(int triesSize, CatchHandler[] catchHandlers) { 686 Try[] result = new Try[triesSize]; 687 for (int i = 0; i < triesSize; i++) { 688 int startAddress = readInt(); 689 int instructionCount = readUnsignedShort(); 690 int handlerOffset = readUnsignedShort(); 691 int catchHandlerIndex = findCatchHandlerIndex(catchHandlers, handlerOffset); 692 result[i] = new Try(startAddress, instructionCount, catchHandlerIndex); 693 } 694 return result; 695 } 696 697 private int findCatchHandlerIndex(CatchHandler[] catchHandlers, int offset) { 698 for (int i = 0; i < catchHandlers.length; i++) { 699 CatchHandler catchHandler = catchHandlers[i]; 700 if (catchHandler.getOffset() == offset) { 701 return i; 702 } 703 } 704 throw new IllegalArgumentException(); 705 } 706 707 private CatchHandler readCatchHandler(int offset) { 708 int size = readSleb128(); 709 int handlersCount = Math.abs(size); 710 int[] typeIndexes = new int[handlersCount]; 711 int[] addresses = new int[handlersCount]; 712 for (int i = 0; i < handlersCount; i++) { 713 typeIndexes[i] = readUleb128(); 714 addresses[i] = readUleb128(); 715 } 716 int catchAllAddress = size <= 0 ? readUleb128() : -1; 717 return new CatchHandler(typeIndexes, addresses, catchAllAddress, offset); 718 } 719 720 private ClassData readClassData() { 721 int staticFieldsSize = readUleb128(); 722 int instanceFieldsSize = readUleb128(); 723 int directMethodsSize = readUleb128(); 724 int virtualMethodsSize = readUleb128(); 725 ClassData.Field[] staticFields = readFields(staticFieldsSize); 726 ClassData.Field[] instanceFields = readFields(instanceFieldsSize); 727 ClassData.Method[] directMethods = readMethods(directMethodsSize); 728 ClassData.Method[] virtualMethods = readMethods(virtualMethodsSize); 729 return new ClassData(staticFields, instanceFields, directMethods, virtualMethods); 730 } 731 732 private ClassData.Field[] readFields(int count) { 733 ClassData.Field[] result = new ClassData.Field[count]; 734 int fieldIndex = 0; 735 for (int i = 0; i < count; i++) { 736 fieldIndex += readUleb128(); // field index diff 737 int accessFlags = readUleb128(); 738 result[i] = new ClassData.Field(fieldIndex, accessFlags); 739 } 740 return result; 741 } 742 743 private ClassData.Method[] readMethods(int count) { 744 ClassData.Method[] result = new ClassData.Method[count]; 745 int methodIndex = 0; 746 for (int i = 0; i < count; i++) { 747 methodIndex += readUleb128(); // method index diff 748 int accessFlags = readUleb128(); 749 int codeOff = readUleb128(); 750 result[i] = new ClassData.Method(methodIndex, accessFlags, codeOff); 751 } 752 return result; 753 } 754 755 /** 756 * Returns a byte array containing the bytes from {@code start} to this 757 * section's current position. 758 */ 759 private byte[] getBytesFrom(int start) { 760 int end = data.position(); 761 byte[] result = new byte[end - start]; 762 data.position(start); 763 data.get(result); 764 return result; 765 } 766 767 public Annotation readAnnotation() { 768 byte visibility = readByte(); 769 int start = data.position(); 770 new EncodedValueReader(this, EncodedValueReader.ENCODED_ANNOTATION).skipValue(); 771 return new Annotation(Dex.this, visibility, new EncodedValue(getBytesFrom(start))); 772 } 773 774 public EncodedValue readEncodedArray() { 775 int start = data.position(); 776 new EncodedValueReader(this, EncodedValueReader.ENCODED_ARRAY).skipValue(); 777 return new EncodedValue(getBytesFrom(start)); 778 } 779 780 public void skip(int count) { 781 if (count < 0) { 782 throw new IllegalArgumentException(); 783 } 784 data.position(data.position() + count); 785 } 786 787 /** 788 * Skips bytes until the position is aligned to a multiple of 4. 789 */ 790 public void alignToFourBytes() { 791 data.position((data.position() + 3) & ~3); 792 } 793 794 /** 795 * Writes 0x00 until the position is aligned to a multiple of 4. 796 */ 797 public void alignToFourBytesWithZeroFill() { 798 while ((data.position() & 3) != 0) { 799 data.put((byte) 0); 800 } 801 } 802 803 public void assertFourByteAligned() { 804 if ((data.position() & 3) != 0) { 805 throw new IllegalStateException("Not four byte aligned!"); 806 } 807 } 808 809 public void write(byte[] bytes) { 810 this.data.put(bytes); 811 } 812 813 public void writeByte(int b) { 814 data.put((byte) b); 815 } 816 817 public void writeShort(short i) { 818 data.putShort(i); 819 } 820 821 public void writeUnsignedShort(int i) { 822 short s = (short) i; 823 if (i != (s & 0xffff)) { 824 throw new IllegalArgumentException("Expected an unsigned short: " + i); 825 } 826 writeShort(s); 827 } 828 829 public void write(short[] shorts) { 830 for (short s : shorts) { 831 writeShort(s); 832 } 833 } 834 835 public void writeInt(int i) { 836 data.putInt(i); 837 } 838 839 public void writeUleb128(int i) { 840 try { 841 Leb128.writeUnsignedLeb128(this, i); 842 } catch (ArrayIndexOutOfBoundsException e) { 843 throw new DexException("Section limit " + data.limit() + " exceeded by " + name); 844 } 845 } 846 847 public void writeSleb128(int i) { 848 try { 849 Leb128.writeSignedLeb128(this, i); 850 } catch (ArrayIndexOutOfBoundsException e) { 851 throw new DexException("Section limit " + data.limit() + " exceeded by " + name); 852 } 853 } 854 855 public void writeStringData(String value) { 856 try { 857 int length = value.length(); 858 writeUleb128(length); 859 write(Mutf8.encode(value)); 860 writeByte(0); 861 } catch (UTFDataFormatException e) { 862 throw new AssertionError(); 863 } 864 } 865 866 public void writeTypeList(TypeList typeList) { 867 short[] types = typeList.getTypes(); 868 writeInt(types.length); 869 for (short type : types) { 870 writeShort(type); 871 } 872 alignToFourBytesWithZeroFill(); 873 } 874 875 /** 876 * Returns the number of bytes remaining in this section. 877 */ 878 public int remaining() { 879 return data.remaining(); 880 } 881 882 /** 883 * Returns the number of bytes used by this section. 884 */ 885 public int used () { 886 return data.position() - initialPosition; 887 } 888 } 889 890 private final class StringTable extends AbstractList<String> implements RandomAccess { 891 @Override public String get(int index) { 892 checkBounds(index, tableOfContents.stringIds.size); 893 return open(tableOfContents.stringIds.off + (index * SizeOf.STRING_ID_ITEM)) 894 .readString(); 895 } 896 @Override public int size() { 897 return tableOfContents.stringIds.size; 898 } 899 }; 900 901 private final class TypeIndexToDescriptorIndexTable extends AbstractList<Integer> 902 implements RandomAccess { 903 @Override public Integer get(int index) { 904 return descriptorIndexFromTypeIndex(index); 905 } 906 @Override public int size() { 907 return tableOfContents.typeIds.size; 908 } 909 }; 910 911 private final class TypeIndexToDescriptorTable extends AbstractList<String> 912 implements RandomAccess { 913 @Override public String get(int index) { 914 return strings.get(descriptorIndexFromTypeIndex(index)); 915 } 916 @Override public int size() { 917 return tableOfContents.typeIds.size; 918 } 919 }; 920 921 private final class ProtoIdTable extends AbstractList<ProtoId> implements RandomAccess { 922 @Override public ProtoId get(int index) { 923 checkBounds(index, tableOfContents.protoIds.size); 924 return open(tableOfContents.protoIds.off + (SizeOf.PROTO_ID_ITEM * index)) 925 .readProtoId(); 926 } 927 @Override public int size() { 928 return tableOfContents.protoIds.size; 929 } 930 }; 931 932 private final class FieldIdTable extends AbstractList<FieldId> implements RandomAccess { 933 @Override public FieldId get(int index) { 934 checkBounds(index, tableOfContents.fieldIds.size); 935 return open(tableOfContents.fieldIds.off + (SizeOf.MEMBER_ID_ITEM * index)) 936 .readFieldId(); 937 } 938 @Override public int size() { 939 return tableOfContents.fieldIds.size; 940 } 941 }; 942 943 private final class MethodIdTable extends AbstractList<MethodId> implements RandomAccess { 944 @Override public MethodId get(int index) { 945 checkBounds(index, tableOfContents.methodIds.size); 946 return open(tableOfContents.methodIds.off + (SizeOf.MEMBER_ID_ITEM * index)) 947 .readMethodId(); 948 } 949 @Override public int size() { 950 return tableOfContents.methodIds.size; 951 } 952 }; 953 954 private final class ClassDefIterator implements Iterator<ClassDef> { 955 private final Dex.Section in = open(tableOfContents.classDefs.off); 956 private int count = 0; 957 958 @Override 959 public boolean hasNext() { 960 return count < tableOfContents.classDefs.size; 961 } 962 @Override 963 public ClassDef next() { 964 if (!hasNext()) { 965 throw new NoSuchElementException(); 966 } 967 count++; 968 return in.readClassDef(); 969 } 970 @Override 971 public void remove() { 972 throw new UnsupportedOperationException(); 973 } 974 }; 975 976 private final class ClassDefIterable implements Iterable<ClassDef> { 977 public Iterator<ClassDef> iterator() { 978 return !tableOfContents.classDefs.exists() 979 ? Collections.<ClassDef>emptySet().iterator() 980 : new ClassDefIterator(); 981 } 982 }; 983 } 984