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.dx.io; 18 19 import com.android.dx.dex.DexFormat; 20 import com.android.dx.dex.SizeOf; 21 import com.android.dx.dex.TableOfContents; 22 import com.android.dx.merge.TypeList; 23 import com.android.dx.util.ByteInput; 24 import com.android.dx.util.ByteOutput; 25 import com.android.dx.util.DexException; 26 import com.android.dx.util.FileUtils; 27 import com.android.dx.util.Leb128Utils; 28 import com.android.dx.util.Mutf8; 29 import java.io.ByteArrayOutputStream; 30 import java.io.File; 31 import java.io.FileInputStream; 32 import java.io.FileOutputStream; 33 import java.io.IOException; 34 import java.io.InputStream; 35 import java.io.OutputStream; 36 import java.io.UTFDataFormatException; 37 import java.util.AbstractList; 38 import java.util.Arrays; 39 import java.util.Collections; 40 import java.util.Iterator; 41 import java.util.List; 42 import java.util.NoSuchElementException; 43 import java.util.zip.ZipEntry; 44 import java.util.zip.ZipFile; 45 46 /** 47 * The bytes of a dex file in memory for reading and writing. All int offsets 48 * are unsigned. 49 */ 50 public final class DexBuffer { 51 private byte[] data; 52 private final TableOfContents tableOfContents = new TableOfContents(); 53 private int length = 0; 54 55 private final List<String> strings = new AbstractList<String>() { 56 @Override public String get(int index) { 57 checkBounds(index, tableOfContents.stringIds.size); 58 return open(tableOfContents.stringIds.off + (index * SizeOf.STRING_ID_ITEM)) 59 .readString(); 60 } 61 @Override public int size() { 62 return tableOfContents.stringIds.size; 63 } 64 }; 65 66 private final List<Integer> typeIds = new AbstractList<Integer>() { 67 @Override public Integer get(int index) { 68 checkBounds(index, tableOfContents.typeIds.size); 69 return open(tableOfContents.typeIds.off + (index * SizeOf.TYPE_ID_ITEM)).readInt(); 70 } 71 @Override public int size() { 72 return tableOfContents.typeIds.size; 73 } 74 }; 75 76 private final List<String> typeNames = new AbstractList<String>() { 77 @Override public String get(int index) { 78 checkBounds(index, tableOfContents.typeIds.size); 79 return strings.get(typeIds.get(index)); 80 } 81 @Override public int size() { 82 return tableOfContents.typeIds.size; 83 } 84 }; 85 86 private final List<ProtoId> protoIds = new AbstractList<ProtoId>() { 87 @Override public ProtoId get(int index) { 88 checkBounds(index, tableOfContents.protoIds.size); 89 return open(tableOfContents.protoIds.off + (SizeOf.PROTO_ID_ITEM * index)) 90 .readProtoId(); 91 } 92 @Override public int size() { 93 return tableOfContents.protoIds.size; 94 } 95 }; 96 97 private final List<FieldId> fieldIds = new AbstractList<FieldId>() { 98 @Override public FieldId get(int index) { 99 checkBounds(index, tableOfContents.fieldIds.size); 100 return open(tableOfContents.fieldIds.off + (SizeOf.MEMBER_ID_ITEM * index)) 101 .readFieldId(); 102 } 103 @Override public int size() { 104 return tableOfContents.fieldIds.size; 105 } 106 }; 107 108 private final List<MethodId> methodIds = new AbstractList<MethodId>() { 109 @Override public MethodId get(int index) { 110 checkBounds(index, tableOfContents.methodIds.size); 111 return open(tableOfContents.methodIds.off + (SizeOf.MEMBER_ID_ITEM * index)) 112 .readMethodId(); 113 } 114 @Override public int size() { 115 return tableOfContents.methodIds.size; 116 } 117 }; 118 119 /** 120 * Creates a new dex buffer defining no classes. 121 */ DexBuffer()122 public DexBuffer() { 123 this.data = new byte[0]; 124 } 125 126 /** 127 * Creates a new dex buffer that reads from {@code data}. It is an error to 128 * modify {@code data} after using it to create a dex buffer. 129 */ DexBuffer(byte[] data)130 public DexBuffer(byte[] data) throws IOException { 131 this.data = data; 132 this.length = data.length; 133 this.tableOfContents.readFrom(this); 134 } 135 136 /** 137 * Creates a new dex buffer of the dex in {@code in}, and closes {@code in}. 138 */ DexBuffer(InputStream in)139 public DexBuffer(InputStream in) throws IOException { 140 loadFrom(in); 141 } 142 143 /** 144 * Creates a new dex buffer from the dex file {@code file}. 145 */ DexBuffer(File file)146 public DexBuffer(File file) throws IOException { 147 if (FileUtils.hasArchiveSuffix(file.getName())) { 148 ZipFile zipFile = new ZipFile(file); 149 ZipEntry entry = zipFile.getEntry(DexFormat.DEX_IN_JAR_NAME); 150 if (entry != null) { 151 loadFrom(zipFile.getInputStream(entry)); 152 zipFile.close(); 153 } else { 154 throw new DexException("Expected " + DexFormat.DEX_IN_JAR_NAME + " in " + file); 155 } 156 } else if (file.getName().endsWith(".dex")) { 157 loadFrom(new FileInputStream(file)); 158 } else { 159 throw new DexException("unknown output extension: " + file); 160 } 161 } 162 loadFrom(InputStream in)163 private void loadFrom(InputStream in) throws IOException { 164 ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); 165 byte[] buffer = new byte[8192]; 166 167 int count; 168 while ((count = in.read(buffer)) != -1) { 169 bytesOut.write(buffer, 0, count); 170 } 171 in.close(); 172 173 this.data = bytesOut.toByteArray(); 174 this.length = data.length; 175 this.tableOfContents.readFrom(this); 176 } 177 checkBounds(int index, int length)178 private static void checkBounds(int index, int length) { 179 if (index < 0 || index >= length) { 180 throw new IndexOutOfBoundsException("index:" + index + ", length=" + length); 181 } 182 } 183 writeTo(OutputStream out)184 public void writeTo(OutputStream out) throws IOException { 185 out.write(data); 186 } 187 writeTo(File dexOut)188 public void writeTo(File dexOut) throws IOException { 189 OutputStream out = new FileOutputStream(dexOut); 190 writeTo(out); 191 out.close(); 192 } 193 getTableOfContents()194 public TableOfContents getTableOfContents() { 195 return tableOfContents; 196 } 197 open(int position)198 public Section open(int position) { 199 if (position < 0 || position > length) { 200 throw new IllegalArgumentException("position=" + position + " length=" + length); 201 } 202 return new Section(position); 203 } 204 appendSection(int maxByteCount, String name)205 public Section appendSection(int maxByteCount, String name) { 206 int limit = fourByteAlign(length + maxByteCount); 207 Section result = new Section(name, length, limit); 208 length = limit; 209 return result; 210 } 211 noMoreSections()212 public void noMoreSections() { 213 data = new byte[length]; 214 } 215 getLength()216 public int getLength() { 217 return length; 218 } 219 fourByteAlign(int position)220 public static int fourByteAlign(int position) { 221 return (position + 3) & ~3; 222 } 223 getBytes()224 public byte[] getBytes() { 225 return data; 226 } 227 strings()228 public List<String> strings() { 229 return strings; 230 } 231 typeIds()232 public List<Integer> typeIds() { 233 return typeIds; 234 } 235 typeNames()236 public List<String> typeNames() { 237 return typeNames; 238 } 239 protoIds()240 public List<ProtoId> protoIds() { 241 return protoIds; 242 } 243 fieldIds()244 public List<FieldId> fieldIds() { 245 return fieldIds; 246 } 247 methodIds()248 public List<MethodId> methodIds() { 249 return methodIds; 250 } 251 classDefs()252 public Iterable<ClassDef> classDefs() { 253 return new Iterable<ClassDef>() { 254 public Iterator<ClassDef> iterator() { 255 if (!tableOfContents.classDefs.exists()) { 256 return Collections.<ClassDef>emptySet().iterator(); 257 } 258 return new Iterator<ClassDef>() { 259 private DexBuffer.Section in = open(tableOfContents.classDefs.off); 260 private int count = 0; 261 262 public boolean hasNext() { 263 return count < tableOfContents.classDefs.size; 264 } 265 public ClassDef next() { 266 if (!hasNext()) { 267 throw new NoSuchElementException(); 268 } 269 count++; 270 return in.readClassDef(); 271 } 272 public void remove() { 273 throw new UnsupportedOperationException(); 274 } 275 }; 276 } 277 }; 278 } 279 280 public TypeList readTypeList(int offset) { 281 if (offset == 0) { 282 return TypeList.EMPTY; 283 } 284 return open(offset).readTypeList(); 285 } 286 287 public ClassData readClassData(ClassDef classDef) { 288 int offset = classDef.getClassDataOffset(); 289 if (offset == 0) { 290 throw new IllegalArgumentException("offset == 0"); 291 } 292 return open(offset).readClassData(); 293 } 294 295 public Code readCode(ClassData.Method method) { 296 int offset = method.getCodeOffset(); 297 if (offset == 0) { 298 throw new IllegalArgumentException("offset == 0"); 299 } 300 return open(offset).readCode(); 301 } 302 303 public final class Section implements ByteInput, ByteOutput { 304 private final String name; 305 private int position; 306 private final int limit; 307 private final int initialPosition; 308 309 private Section(String name, int position, int limit) { 310 this.name = name; 311 this.position = this.initialPosition = position; 312 this.limit = limit; 313 } 314 315 private Section(int position) { 316 this("section", position, data.length); 317 } 318 319 public int getPosition() { 320 return position; 321 } 322 323 public int readInt() { 324 int result = (data[position] & 0xff) 325 | (data[position + 1] & 0xff) << 8 326 | (data[position + 2] & 0xff) << 16 327 | (data[position + 3] & 0xff) << 24; 328 position += 4; 329 return result; 330 } 331 332 public short readShort() { 333 int result = (data[position] & 0xff) 334 | (data[position + 1] & 0xff) << 8; 335 position += 2; 336 return (short) result; 337 } 338 339 public int readUnsignedShort() { 340 return readShort() & 0xffff; 341 } 342 343 public byte readByte() { 344 return (byte) (data[position++] & 0xff); 345 } 346 347 public byte[] readByteArray(int length) { 348 byte[] result = Arrays.copyOfRange(data, position, position + length); 349 position += length; 350 return result; 351 } 352 353 public short[] readShortArray(int length) { 354 short[] result = new short[length]; 355 for (int i = 0; i < length; i++) { 356 result[i] = readShort(); 357 } 358 return result; 359 } 360 361 public int readUleb128() { 362 return Leb128Utils.readUnsignedLeb128(this); 363 } 364 365 public int readSleb128() { 366 return Leb128Utils.readSignedLeb128(this); 367 } 368 369 public TypeList readTypeList() { 370 int size = readInt(); 371 short[] types = new short[size]; 372 for (int i = 0; i < size; i++) { 373 types[i] = readShort(); 374 } 375 alignToFourBytes(); 376 return new TypeList(DexBuffer.this, types); 377 } 378 379 public String readString() { 380 int offset = readInt(); 381 int savedPosition = position; 382 position = offset; 383 try { 384 int expectedLength = readUleb128(); 385 String result = Mutf8.decode(this, new char[expectedLength]); 386 if (result.length() != expectedLength) { 387 throw new DexException("Declared length " + expectedLength 388 + " doesn't match decoded length of " + result.length()); 389 } 390 return result; 391 } catch (UTFDataFormatException e) { 392 throw new DexException(e); 393 } finally { 394 position = savedPosition; 395 } 396 } 397 398 public FieldId readFieldId() { 399 int declaringClassIndex = readUnsignedShort(); 400 int typeIndex = readUnsignedShort(); 401 int nameIndex = readInt(); 402 return new FieldId(DexBuffer.this, declaringClassIndex, typeIndex, nameIndex); 403 } 404 405 public MethodId readMethodId() { 406 int declaringClassIndex = readUnsignedShort(); 407 int protoIndex = readUnsignedShort(); 408 int nameIndex = readInt(); 409 return new MethodId(DexBuffer.this, declaringClassIndex, protoIndex, nameIndex); 410 } 411 412 public ProtoId readProtoId() { 413 int shortyIndex = readInt(); 414 int returnTypeIndex = readInt(); 415 int parametersOffset = readInt(); 416 return new ProtoId(DexBuffer.this, shortyIndex, returnTypeIndex, parametersOffset); 417 } 418 419 public ClassDef readClassDef() { 420 int offset = getPosition(); 421 int type = readInt(); 422 int accessFlags = readInt(); 423 int supertype = readInt(); 424 int interfacesOffset = readInt(); 425 int sourceFileIndex = readInt(); 426 int annotationsOffset = readInt(); 427 int classDataOffset = readInt(); 428 int staticValuesOffset = readInt(); 429 return new ClassDef(DexBuffer.this, offset, type, accessFlags, supertype, 430 interfacesOffset, sourceFileIndex, annotationsOffset, classDataOffset, 431 staticValuesOffset); 432 } 433 434 private Code readCode() { 435 int registersSize = readUnsignedShort(); 436 int insSize = readUnsignedShort(); 437 int outsSize = readUnsignedShort(); 438 int triesSize = readUnsignedShort(); 439 int debugInfoOffset = readInt(); 440 int instructionsSize = readInt(); 441 short[] instructions = readShortArray(instructionsSize); 442 Code.Try[] tries = new Code.Try[triesSize]; 443 Code.CatchHandler[] catchHandlers = new Code.CatchHandler[0]; 444 if (triesSize > 0) { 445 if (instructions.length % 2 == 1) { 446 readShort(); // padding 447 } 448 449 for (int i = 0; i < triesSize; i++) { 450 int startAddress = readInt(); 451 int instructionCount = readUnsignedShort(); 452 int handlerOffset = readUnsignedShort(); 453 tries[i] = new Code.Try(startAddress, instructionCount, handlerOffset); 454 } 455 456 int catchHandlersSize = readUleb128(); 457 catchHandlers = new Code.CatchHandler[catchHandlersSize]; 458 for (int i = 0; i < catchHandlersSize; i++) { 459 catchHandlers[i] = readCatchHandler(); 460 } 461 } 462 return new Code(registersSize, insSize, outsSize, debugInfoOffset, instructions, 463 tries, catchHandlers); 464 } 465 466 private Code.CatchHandler readCatchHandler() { 467 int size = readSleb128(); 468 int handlersCount = Math.abs(size); 469 int[] typeIndexes = new int[handlersCount]; 470 int[] addresses = new int[handlersCount]; 471 for (int i = 0; i < handlersCount; i++) { 472 typeIndexes[i] = readUleb128(); 473 addresses[i] = readUleb128(); 474 } 475 int catchAllAddress = size <= 0 ? readUleb128() : -1; 476 return new Code.CatchHandler(typeIndexes, addresses, catchAllAddress); 477 } 478 479 private ClassData readClassData() { 480 int staticFieldsSize = readUleb128(); 481 int instanceFieldsSize = readUleb128(); 482 int directMethodsSize = readUleb128(); 483 int virtualMethodsSize = readUleb128(); 484 ClassData.Field[] staticFields = readFields(staticFieldsSize); 485 ClassData.Field[] instanceFields = readFields(instanceFieldsSize); 486 ClassData.Method[] directMethods = readMethods(directMethodsSize); 487 ClassData.Method[] virtualMethods = readMethods(virtualMethodsSize); 488 return new ClassData(staticFields, instanceFields, directMethods, virtualMethods); 489 } 490 491 private ClassData.Field[] readFields(int count) { 492 ClassData.Field[] result = new ClassData.Field[count]; 493 int fieldIndex = 0; 494 for (int i = 0; i < count; i++) { 495 fieldIndex += readUleb128(); // field index diff 496 int accessFlags = readUleb128(); 497 result[i] = new ClassData.Field(fieldIndex, accessFlags); 498 } 499 return result; 500 } 501 502 private ClassData.Method[] readMethods(int count) { 503 ClassData.Method[] result = new ClassData.Method[count]; 504 int methodIndex = 0; 505 for (int i = 0; i < count; i++) { 506 methodIndex += readUleb128(); // method index diff 507 int accessFlags = readUleb128(); 508 int codeOff = readUleb128(); 509 result[i] = new ClassData.Method(methodIndex, accessFlags, codeOff); 510 } 511 return result; 512 } 513 514 public Annotation readAnnotation() { 515 byte visibility = readByte(); 516 int typeIndex = readUleb128(); 517 int size = readUleb128(); 518 int[] names = new int[size]; 519 EncodedValue[] values = new EncodedValue[size]; 520 for (int i = 0; i < size; i++) { 521 names[i] = readUleb128(); 522 values[i] = readEncodedValue(); 523 } 524 return new Annotation(DexBuffer.this, visibility, typeIndex, names, values); 525 } 526 527 public EncodedValue readEncodedValue() { 528 int start = position; 529 new EncodedValueReader(this).readValue(); 530 int end = position; 531 return new EncodedValue(Arrays.copyOfRange(data, start, end)); 532 } 533 534 public EncodedValue readEncodedArray() { 535 int start = position; 536 new EncodedValueReader(this).readArray(); 537 int end = position; 538 return new EncodedValue(Arrays.copyOfRange(data, start, end)); 539 } 540 541 private void ensureCapacity(int size) { 542 if (position + size > limit) { 543 throw new DexException("Section limit " + limit + " exceeded by " + name); 544 } 545 } 546 547 /** 548 * Writes 0x00 until the position is aligned to a multiple of 4. 549 */ 550 public void alignToFourBytes() { 551 int unalignedCount = position; 552 position = DexBuffer.fourByteAlign(position); 553 for (int i = unalignedCount; i < position; i++) { 554 data[i] = 0; 555 } 556 } 557 558 public void assertFourByteAligned() { 559 if ((position & 3) != 0) { 560 throw new IllegalStateException("Not four byte aligned!"); 561 } 562 } 563 564 public void write(byte[] bytes) { 565 ensureCapacity(bytes.length); 566 System.arraycopy(bytes, 0, data, position, bytes.length); 567 position += bytes.length; 568 } 569 570 public void writeByte(int b) { 571 ensureCapacity(1); 572 data[position++] = (byte) b; 573 } 574 575 public void writeShort(short i) { 576 ensureCapacity(2); 577 data[position ] = (byte) i; 578 data[position + 1] = (byte) (i >>> 8); 579 position += 2; 580 } 581 582 public void writeUnsignedShort(int i) { 583 short s = (short) i; 584 if (i != (s & 0xffff)) { 585 throw new IllegalArgumentException("Expected an unsigned short: " + i); 586 } 587 writeShort(s); 588 } 589 590 public void write(short[] shorts) { 591 for (short s : shorts) { 592 writeShort(s); 593 } 594 } 595 596 public void writeInt(int i) { 597 ensureCapacity(4); 598 data[position ] = (byte) i; 599 data[position + 1] = (byte) (i >>> 8); 600 data[position + 2] = (byte) (i >>> 16); 601 data[position + 3] = (byte) (i >>> 24); 602 position += 4; 603 } 604 605 public void writeUleb128(int i) { 606 try { 607 Leb128Utils.writeUnsignedLeb128(this, i); 608 ensureCapacity(0); 609 } catch (ArrayIndexOutOfBoundsException e) { 610 throw new DexException("Section limit " + limit + " exceeded by " + name); 611 } 612 } 613 614 public void writeSleb128(int i) { 615 try { 616 Leb128Utils.writeSignedLeb128(this, i); 617 ensureCapacity(0); 618 } catch (ArrayIndexOutOfBoundsException e) { 619 throw new DexException("Section limit " + limit + " exceeded by " + name); 620 } 621 } 622 623 public void writeStringData(String value) { 624 try { 625 int length = value.length(); 626 writeUleb128(length); 627 write(Mutf8.encode(value)); 628 writeByte(0); 629 } catch (UTFDataFormatException e) { 630 throw new AssertionError(); 631 } 632 } 633 634 public void writeTypeList(TypeList typeList) { 635 short[] types = typeList.getTypes(); 636 writeInt(types.length); 637 for (short type : types) { 638 writeShort(type); 639 } 640 alignToFourBytes(); 641 } 642 643 /** 644 * Returns the number of bytes remaining in this section. 645 */ 646 public int remaining() { 647 return limit - position; 648 } 649 650 /** 651 * Returns the number of bytes used by this section. 652 */ 653 public int used () { 654 return position - initialPosition; 655 } 656 } 657 } 658