1 /* 2 * Copyright (C) 2009 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.dexdeps; 18 19 import java.io.IOException; 20 import java.io.RandomAccessFile; 21 import java.util.Arrays; 22 23 /** 24 * Data extracted from a DEX file. 25 */ 26 public class DexData { 27 private RandomAccessFile mDexFile; 28 private HeaderItem mHeaderItem; 29 private String[] mStrings; // strings from string_data_* 30 private TypeIdItem[] mTypeIds; 31 private ProtoIdItem[] mProtoIds; 32 private FieldIdItem[] mFieldIds; 33 private MethodIdItem[] mMethodIds; 34 private ClassDefItem[] mClassDefs; 35 36 private byte tmpBuf[] = new byte[4]; 37 private boolean isBigEndian = false; 38 39 /** 40 * Constructs a new DexData for this file. 41 */ DexData(RandomAccessFile raf)42 public DexData(RandomAccessFile raf) { 43 mDexFile = raf; 44 } 45 46 /** 47 * Loads the contents of the DEX file into our data structures. 48 * 49 * @throws IOException if we encounter a problem while reading 50 * @throws DexDataException if the DEX contents look bad 51 */ load()52 public void load() throws IOException { 53 parseHeaderItem(); 54 55 loadStrings(); 56 loadTypeIds(); 57 loadProtoIds(); 58 loadFieldIds(); 59 loadMethodIds(); 60 loadClassDefs(); 61 62 markInternalClasses(); 63 } 64 65 /** 66 * Verifies the given magic number. 67 */ verifyMagic(byte[] magic)68 private static boolean verifyMagic(byte[] magic) { 69 return Arrays.equals(magic, HeaderItem.DEX_FILE_MAGIC) || 70 Arrays.equals(magic, HeaderItem.DEX_FILE_MAGIC_API_13); 71 } 72 73 /** 74 * Parses the interesting bits out of the header. 75 */ parseHeaderItem()76 void parseHeaderItem() throws IOException { 77 mHeaderItem = new HeaderItem(); 78 79 seek(0); 80 81 byte[] magic = new byte[8]; 82 readBytes(magic); 83 if (!verifyMagic(magic)) { 84 System.err.println("Magic number is wrong -- are you sure " + 85 "this is a DEX file?"); 86 throw new DexDataException(); 87 } 88 89 /* 90 * Read the endian tag, so we properly swap things as we read 91 * them from here on. 92 */ 93 seek(8+4+20+4+4); 94 mHeaderItem.endianTag = readInt(); 95 if (mHeaderItem.endianTag == HeaderItem.ENDIAN_CONSTANT) { 96 /* do nothing */ 97 } else if (mHeaderItem.endianTag == HeaderItem.REVERSE_ENDIAN_CONSTANT){ 98 /* file is big-endian (!), reverse future reads */ 99 isBigEndian = true; 100 } else { 101 System.err.println("Endian constant has unexpected value " + 102 Integer.toHexString(mHeaderItem.endianTag)); 103 throw new DexDataException(); 104 } 105 106 seek(8+4+20); // magic, checksum, signature 107 mHeaderItem.fileSize = readInt(); 108 mHeaderItem.headerSize = readInt(); 109 /*mHeaderItem.endianTag =*/ readInt(); 110 /*mHeaderItem.linkSize =*/ readInt(); 111 /*mHeaderItem.linkOff =*/ readInt(); 112 /*mHeaderItem.mapOff =*/ readInt(); 113 mHeaderItem.stringIdsSize = readInt(); 114 mHeaderItem.stringIdsOff = readInt(); 115 mHeaderItem.typeIdsSize = readInt(); 116 mHeaderItem.typeIdsOff = readInt(); 117 mHeaderItem.protoIdsSize = readInt(); 118 mHeaderItem.protoIdsOff = readInt(); 119 mHeaderItem.fieldIdsSize = readInt(); 120 mHeaderItem.fieldIdsOff = readInt(); 121 mHeaderItem.methodIdsSize = readInt(); 122 mHeaderItem.methodIdsOff = readInt(); 123 mHeaderItem.classDefsSize = readInt(); 124 mHeaderItem.classDefsOff = readInt(); 125 /*mHeaderItem.dataSize =*/ readInt(); 126 /*mHeaderItem.dataOff =*/ readInt(); 127 } 128 129 /** 130 * Loads the string table out of the DEX. 131 * 132 * First we read all of the string_id_items, then we read all of the 133 * string_data_item. Doing it this way should allow us to avoid 134 * seeking around in the file. 135 */ loadStrings()136 void loadStrings() throws IOException { 137 int count = mHeaderItem.stringIdsSize; 138 int stringOffsets[] = new int[count]; 139 140 //System.out.println("reading " + count + " strings"); 141 142 seek(mHeaderItem.stringIdsOff); 143 for (int i = 0; i < count; i++) { 144 stringOffsets[i] = readInt(); 145 } 146 147 mStrings = new String[count]; 148 149 seek(stringOffsets[0]); 150 for (int i = 0; i < count; i++) { 151 seek(stringOffsets[i]); // should be a no-op 152 mStrings[i] = readString(); 153 //System.out.println("STR: " + i + ": " + mStrings[i]); 154 } 155 } 156 157 /** 158 * Loads the type ID list. 159 */ loadTypeIds()160 void loadTypeIds() throws IOException { 161 int count = mHeaderItem.typeIdsSize; 162 mTypeIds = new TypeIdItem[count]; 163 164 //System.out.println("reading " + count + " typeIds"); 165 seek(mHeaderItem.typeIdsOff); 166 for (int i = 0; i < count; i++) { 167 mTypeIds[i] = new TypeIdItem(); 168 mTypeIds[i].descriptorIdx = readInt(); 169 170 //System.out.println(i + ": " + mTypeIds[i].descriptorIdx + 171 // " " + mStrings[mTypeIds[i].descriptorIdx]); 172 } 173 } 174 175 /** 176 * Loads the proto ID list. 177 */ loadProtoIds()178 void loadProtoIds() throws IOException { 179 int count = mHeaderItem.protoIdsSize; 180 mProtoIds = new ProtoIdItem[count]; 181 182 //System.out.println("reading " + count + " protoIds"); 183 seek(mHeaderItem.protoIdsOff); 184 185 /* 186 * Read the proto ID items. 187 */ 188 for (int i = 0; i < count; i++) { 189 mProtoIds[i] = new ProtoIdItem(); 190 mProtoIds[i].shortyIdx = readInt(); 191 mProtoIds[i].returnTypeIdx = readInt(); 192 mProtoIds[i].parametersOff = readInt(); 193 194 //System.out.println(i + ": " + mProtoIds[i].shortyIdx + 195 // " " + mStrings[mProtoIds[i].shortyIdx]); 196 } 197 198 /* 199 * Go back through and read the type lists. 200 */ 201 for (int i = 0; i < count; i++) { 202 ProtoIdItem protoId = mProtoIds[i]; 203 204 int offset = protoId.parametersOff; 205 206 if (offset == 0) { 207 protoId.types = new int[0]; 208 continue; 209 } else { 210 seek(offset); 211 int size = readInt(); // #of entries in list 212 protoId.types = new int[size]; 213 214 for (int j = 0; j < size; j++) { 215 protoId.types[j] = readShort() & 0xffff; 216 } 217 } 218 } 219 } 220 221 /** 222 * Loads the field ID list. 223 */ loadFieldIds()224 void loadFieldIds() throws IOException { 225 int count = mHeaderItem.fieldIdsSize; 226 mFieldIds = new FieldIdItem[count]; 227 228 //System.out.println("reading " + count + " fieldIds"); 229 seek(mHeaderItem.fieldIdsOff); 230 for (int i = 0; i < count; i++) { 231 mFieldIds[i] = new FieldIdItem(); 232 mFieldIds[i].classIdx = readShort() & 0xffff; 233 mFieldIds[i].typeIdx = readShort() & 0xffff; 234 mFieldIds[i].nameIdx = readInt(); 235 236 //System.out.println(i + ": " + mFieldIds[i].nameIdx + 237 // " " + mStrings[mFieldIds[i].nameIdx]); 238 } 239 } 240 241 /** 242 * Loads the method ID list. 243 */ loadMethodIds()244 void loadMethodIds() throws IOException { 245 int count = mHeaderItem.methodIdsSize; 246 mMethodIds = new MethodIdItem[count]; 247 248 //System.out.println("reading " + count + " methodIds"); 249 seek(mHeaderItem.methodIdsOff); 250 for (int i = 0; i < count; i++) { 251 mMethodIds[i] = new MethodIdItem(); 252 mMethodIds[i].classIdx = readShort() & 0xffff; 253 mMethodIds[i].protoIdx = readShort() & 0xffff; 254 mMethodIds[i].nameIdx = readInt(); 255 256 //System.out.println(i + ": " + mMethodIds[i].nameIdx + 257 // " " + mStrings[mMethodIds[i].nameIdx]); 258 } 259 } 260 261 /** 262 * Loads the class defs list. 263 */ loadClassDefs()264 void loadClassDefs() throws IOException { 265 int count = mHeaderItem.classDefsSize; 266 mClassDefs = new ClassDefItem[count]; 267 268 //System.out.println("reading " + count + " classDefs"); 269 seek(mHeaderItem.classDefsOff); 270 for (int i = 0; i < count; i++) { 271 mClassDefs[i] = new ClassDefItem(); 272 mClassDefs[i].classIdx = readInt(); 273 274 /* access_flags = */ readInt(); 275 /* superclass_idx = */ readInt(); 276 /* interfaces_off = */ readInt(); 277 /* source_file_idx = */ readInt(); 278 /* annotations_off = */ readInt(); 279 /* class_data_off = */ readInt(); 280 /* static_values_off = */ readInt(); 281 282 //System.out.println(i + ": " + mClassDefs[i].classIdx + " " + 283 // mStrings[mTypeIds[mClassDefs[i].classIdx].descriptorIdx]); 284 } 285 } 286 287 /** 288 * Sets the "internal" flag on type IDs which are defined in the 289 * DEX file or within the VM (e.g. primitive classes and arrays). 290 */ markInternalClasses()291 void markInternalClasses() { 292 for (int i = mClassDefs.length -1; i >= 0; i--) { 293 mTypeIds[mClassDefs[i].classIdx].internal = true; 294 } 295 296 for (int i = 0; i < mTypeIds.length; i++) { 297 String className = mStrings[mTypeIds[i].descriptorIdx]; 298 299 if (className.length() == 1) { 300 // primitive class 301 mTypeIds[i].internal = true; 302 } else if (className.charAt(0) == '[') { 303 mTypeIds[i].internal = true; 304 } 305 306 //System.out.println(i + " " + 307 // (mTypeIds[i].internal ? "INTERNAL" : "external") + " - " + 308 // mStrings[mTypeIds[i].descriptorIdx]); 309 } 310 } 311 312 313 /* 314 * ======================================================================= 315 * Queries 316 * ======================================================================= 317 */ 318 319 /** 320 * Returns the class name, given an index into the type_ids table. 321 */ classNameFromTypeIndex(int idx)322 private String classNameFromTypeIndex(int idx) { 323 return mStrings[mTypeIds[idx].descriptorIdx]; 324 } 325 326 /** 327 * Returns an array of method argument type strings, given an index 328 * into the proto_ids table. 329 */ argArrayFromProtoIndex(int idx)330 private String[] argArrayFromProtoIndex(int idx) { 331 ProtoIdItem protoId = mProtoIds[idx]; 332 String[] result = new String[protoId.types.length]; 333 334 for (int i = 0; i < protoId.types.length; i++) { 335 result[i] = mStrings[mTypeIds[protoId.types[i]].descriptorIdx]; 336 } 337 338 return result; 339 } 340 341 /** 342 * Returns a string representing the method's return type, given an 343 * index into the proto_ids table. 344 */ returnTypeFromProtoIndex(int idx)345 private String returnTypeFromProtoIndex(int idx) { 346 ProtoIdItem protoId = mProtoIds[idx]; 347 return mStrings[mTypeIds[protoId.returnTypeIdx].descriptorIdx]; 348 } 349 350 /** 351 * Returns an array with all of the class references that don't 352 * correspond to classes in the DEX file. Each class reference has 353 * a list of the referenced fields and methods associated with 354 * that class. 355 */ getExternalReferences()356 public ClassRef[] getExternalReferences() { 357 // create a sparse array of ClassRef that parallels mTypeIds 358 ClassRef[] sparseRefs = new ClassRef[mTypeIds.length]; 359 360 // create entries for all externally-referenced classes 361 int count = 0; 362 for (int i = 0; i < mTypeIds.length; i++) { 363 if (!mTypeIds[i].internal) { 364 sparseRefs[i] = 365 new ClassRef(mStrings[mTypeIds[i].descriptorIdx]); 366 count++; 367 } 368 } 369 370 // add fields and methods to the appropriate class entry 371 addExternalFieldReferences(sparseRefs); 372 addExternalMethodReferences(sparseRefs); 373 374 // crunch out the sparseness 375 ClassRef[] classRefs = new ClassRef[count]; 376 int idx = 0; 377 for (int i = 0; i < mTypeIds.length; i++) { 378 if (sparseRefs[i] != null) 379 classRefs[idx++] = sparseRefs[i]; 380 } 381 382 assert idx == count; 383 384 return classRefs; 385 } 386 387 /** 388 * Runs through the list of field references, inserting external 389 * references into the appropriate ClassRef. 390 */ addExternalFieldReferences(ClassRef[] sparseRefs)391 private void addExternalFieldReferences(ClassRef[] sparseRefs) { 392 for (int i = 0; i < mFieldIds.length; i++) { 393 if (!mTypeIds[mFieldIds[i].classIdx].internal) { 394 FieldIdItem fieldId = mFieldIds[i]; 395 FieldRef newFieldRef = new FieldRef( 396 classNameFromTypeIndex(fieldId.classIdx), 397 classNameFromTypeIndex(fieldId.typeIdx), 398 mStrings[fieldId.nameIdx]); 399 sparseRefs[mFieldIds[i].classIdx].addField(newFieldRef); 400 } 401 } 402 } 403 404 /** 405 * Runs through the list of method references, inserting external 406 * references into the appropriate ClassRef. 407 */ addExternalMethodReferences(ClassRef[] sparseRefs)408 private void addExternalMethodReferences(ClassRef[] sparseRefs) { 409 for (int i = 0; i < mMethodIds.length; i++) { 410 if (!mTypeIds[mMethodIds[i].classIdx].internal) { 411 MethodIdItem methodId = mMethodIds[i]; 412 MethodRef newMethodRef = new MethodRef( 413 classNameFromTypeIndex(methodId.classIdx), 414 argArrayFromProtoIndex(methodId.protoIdx), 415 returnTypeFromProtoIndex(methodId.protoIdx), 416 mStrings[methodId.nameIdx]); 417 sparseRefs[mMethodIds[i].classIdx].addMethod(newMethodRef); 418 } 419 } 420 } 421 422 423 /* 424 * ======================================================================= 425 * Basic I/O functions 426 * ======================================================================= 427 */ 428 429 /** 430 * Seeks the DEX file to the specified absolute position. 431 */ seek(int position)432 void seek(int position) throws IOException { 433 mDexFile.seek(position); 434 } 435 436 /** 437 * Fills the buffer by reading bytes from the DEX file. 438 */ readBytes(byte[] buffer)439 void readBytes(byte[] buffer) throws IOException { 440 mDexFile.readFully(buffer); 441 } 442 443 /** 444 * Reads a single signed byte value. 445 */ readByte()446 byte readByte() throws IOException { 447 mDexFile.readFully(tmpBuf, 0, 1); 448 return tmpBuf[0]; 449 } 450 451 /** 452 * Reads a signed 16-bit integer, byte-swapping if necessary. 453 */ readShort()454 short readShort() throws IOException { 455 mDexFile.readFully(tmpBuf, 0, 2); 456 if (isBigEndian) { 457 return (short) ((tmpBuf[1] & 0xff) | ((tmpBuf[0] & 0xff) << 8)); 458 } else { 459 return (short) ((tmpBuf[0] & 0xff) | ((tmpBuf[1] & 0xff) << 8)); 460 } 461 } 462 463 /** 464 * Reads a signed 32-bit integer, byte-swapping if necessary. 465 */ readInt()466 int readInt() throws IOException { 467 mDexFile.readFully(tmpBuf, 0, 4); 468 469 if (isBigEndian) { 470 return (tmpBuf[3] & 0xff) | ((tmpBuf[2] & 0xff) << 8) | 471 ((tmpBuf[1] & 0xff) << 16) | ((tmpBuf[0] & 0xff) << 24); 472 } else { 473 return (tmpBuf[0] & 0xff) | ((tmpBuf[1] & 0xff) << 8) | 474 ((tmpBuf[2] & 0xff) << 16) | ((tmpBuf[3] & 0xff) << 24); 475 } 476 } 477 478 /** 479 * Reads a variable-length unsigned LEB128 value. Does not attempt to 480 * verify that the value is valid. 481 * 482 * @throws EOFException if we run off the end of the file 483 */ readUnsignedLeb128()484 int readUnsignedLeb128() throws IOException { 485 int result = 0; 486 byte val; 487 488 do { 489 val = readByte(); 490 result = (result << 7) | (val & 0x7f); 491 } while (val < 0); 492 493 return result; 494 } 495 496 /** 497 * Reads a UTF-8 string. 498 * 499 * We don't know how long the UTF-8 string is, so we have to read one 500 * byte at a time. We could make an educated guess based on the 501 * utf16_size and seek back if we get it wrong, but seeking backward 502 * may cause the underlying implementation to reload I/O buffers. 503 */ readString()504 String readString() throws IOException { 505 int utf16len = readUnsignedLeb128(); 506 byte inBuf[] = new byte[utf16len * 3]; // worst case 507 int idx; 508 509 for (idx = 0; idx < inBuf.length; idx++) { 510 byte val = readByte(); 511 if (val == 0) 512 break; 513 inBuf[idx] = val; 514 } 515 516 return new String(inBuf, 0, idx, "UTF-8"); 517 } 518 519 520 /* 521 * ======================================================================= 522 * Internal "structure" declarations 523 * ======================================================================= 524 */ 525 526 /** 527 * Holds the contents of a header_item. 528 */ 529 static class HeaderItem { 530 public int fileSize; 531 public int headerSize; 532 public int endianTag; 533 public int stringIdsSize, stringIdsOff; 534 public int typeIdsSize, typeIdsOff; 535 public int protoIdsSize, protoIdsOff; 536 public int fieldIdsSize, fieldIdsOff; 537 public int methodIdsSize, methodIdsOff; 538 public int classDefsSize, classDefsOff; 539 540 /* expected magic values */ 541 public static final byte[] DEX_FILE_MAGIC = { 542 0x64, 0x65, 0x78, 0x0a, 0x30, 0x33, 0x36, 0x00 }; 543 public static final byte[] DEX_FILE_MAGIC_API_13 = { 544 0x64, 0x65, 0x78, 0x0a, 0x30, 0x33, 0x35, 0x00 }; 545 public static final int ENDIAN_CONSTANT = 0x12345678; 546 public static final int REVERSE_ENDIAN_CONSTANT = 0x78563412; 547 } 548 549 /** 550 * Holds the contents of a type_id_item. 551 * 552 * This is chiefly a list of indices into the string table. We need 553 * some additional bits of data, such as whether or not the type ID 554 * represents a class defined in this DEX, so we use an object for 555 * each instead of a simple integer. (Could use a parallel array, but 556 * since this is a desktop app it's not essential.) 557 */ 558 static class TypeIdItem { 559 public int descriptorIdx; // index into string_ids 560 561 public boolean internal; // defined within this DEX file? 562 } 563 564 /** 565 * Holds the contents of a proto_id_item. 566 */ 567 static class ProtoIdItem { 568 public int shortyIdx; // index into string_ids 569 public int returnTypeIdx; // index into type_ids 570 public int parametersOff; // file offset to a type_list 571 572 public int types[]; // contents of type list 573 } 574 575 /** 576 * Holds the contents of a field_id_item. 577 */ 578 static class FieldIdItem { 579 public int classIdx; // index into type_ids (defining class) 580 public int typeIdx; // index into type_ids (field type) 581 public int nameIdx; // index into string_ids 582 } 583 584 /** 585 * Holds the contents of a method_id_item. 586 */ 587 static class MethodIdItem { 588 public int classIdx; // index into type_ids 589 public int protoIdx; // index into proto_ids 590 public int nameIdx; // index into string_ids 591 } 592 593 /** 594 * Holds the contents of a class_def_item. 595 * 596 * We don't really need a class for this, but there's some stuff in 597 * the class_def_item that we might want later. 598 */ 599 static class ClassDefItem { 600 public int classIdx; // index into type_ids 601 } 602 } 603