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