1 /* 2 * Copyright (C) 2008 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 /* 18 * The "dexdump" tool is intended to mimic "objdump". When possible, use 19 * similar command-line arguments. 20 * 21 * TODO: rework the "plain" output format to be more regexp-friendly 22 * 23 * Differences between XML output and the "current.xml" file: 24 * - classes in same package are not all grouped together; generally speaking 25 * nothing is sorted 26 * - no "deprecated" on fields and methods 27 * - no "value" on fields 28 * - no parameter names 29 * - no generic signatures on parameters, e.g. type="java.lang.Class<?>" 30 * - class shows declared fields and methods; does not show inherited fields 31 */ 32 33 #include "libdex/DexFile.h" 34 35 #include "libdex/CmdUtils.h" 36 #include "libdex/DexCatch.h" 37 #include "libdex/DexClass.h" 38 #include "libdex/DexDebugInfo.h" 39 #include "libdex/DexOpcodes.h" 40 #include "libdex/DexProto.h" 41 #include "libdex/InstrUtils.h" 42 #include "libdex/SysUtil.h" 43 44 #include <stdlib.h> 45 #include <stdio.h> 46 #include <fcntl.h> 47 #include <string.h> 48 #include <unistd.h> 49 #include <getopt.h> 50 #include <errno.h> 51 #include <assert.h> 52 53 static const char* gProgName = "dexdump"; 54 55 enum OutputFormat { 56 OUTPUT_PLAIN = 0, /* default */ 57 OUTPUT_XML, /* fancy */ 58 }; 59 60 /* command-line options */ 61 struct Options { 62 bool checksumOnly; 63 bool disassemble; 64 bool showFileHeaders; 65 bool showSectionHeaders; 66 bool ignoreBadChecksum; 67 bool dumpRegisterMaps; 68 OutputFormat outputFormat; 69 const char* tempFileName; 70 bool exportsOnly; 71 bool verbose; 72 }; 73 74 struct Options gOptions; 75 76 /* basic info about a field or method */ 77 struct FieldMethodInfo { 78 const char* classDescriptor; 79 const char* name; 80 const char* signature; 81 }; 82 83 /* 84 * Get 2 little-endian bytes. 85 */ 86 static inline u2 get2LE(unsigned char const* pSrc) 87 { 88 return pSrc[0] | (pSrc[1] << 8); 89 } 90 91 /* 92 * Get 4 little-endian bytes. 93 */ 94 static inline u4 get4LE(unsigned char const* pSrc) 95 { 96 return pSrc[0] | (pSrc[1] << 8) | (pSrc[2] << 16) | (pSrc[3] << 24); 97 } 98 99 /* 100 * Converts a single-character primitive type into its human-readable 101 * equivalent. 102 */ 103 static const char* primitiveTypeLabel(char typeChar) 104 { 105 switch (typeChar) { 106 case 'B': return "byte"; 107 case 'C': return "char"; 108 case 'D': return "double"; 109 case 'F': return "float"; 110 case 'I': return "int"; 111 case 'J': return "long"; 112 case 'S': return "short"; 113 case 'V': return "void"; 114 case 'Z': return "boolean"; 115 default: 116 return "UNKNOWN"; 117 } 118 } 119 120 /* 121 * Converts a type descriptor to human-readable "dotted" form. For 122 * example, "Ljava/lang/String;" becomes "java.lang.String", and 123 * "[I" becomes "int[]". Also converts '$' to '.', which means this 124 * form can't be converted back to a descriptor. 125 */ 126 static char* descriptorToDot(const char* str) 127 { 128 int targetLen = strlen(str); 129 int offset = 0; 130 int arrayDepth = 0; 131 char* newStr; 132 133 /* strip leading [s; will be added to end */ 134 while (targetLen > 1 && str[offset] == '[') { 135 offset++; 136 targetLen--; 137 } 138 arrayDepth = offset; 139 140 if (targetLen == 1) { 141 /* primitive type */ 142 str = primitiveTypeLabel(str[offset]); 143 offset = 0; 144 targetLen = strlen(str); 145 } else { 146 /* account for leading 'L' and trailing ';' */ 147 if (targetLen >= 2 && str[offset] == 'L' && 148 str[offset+targetLen-1] == ';') 149 { 150 targetLen -= 2; 151 offset++; 152 } 153 } 154 155 newStr = (char*)malloc(targetLen + arrayDepth * 2 +1); 156 157 /* copy class name over */ 158 int i; 159 for (i = 0; i < targetLen; i++) { 160 char ch = str[offset + i]; 161 newStr[i] = (ch == '/' || ch == '$') ? '.' : ch; 162 } 163 164 /* add the appropriate number of brackets for arrays */ 165 while (arrayDepth-- > 0) { 166 newStr[i++] = '['; 167 newStr[i++] = ']'; 168 } 169 newStr[i] = '\0'; 170 assert(i == targetLen + arrayDepth * 2); 171 172 return newStr; 173 } 174 175 /* 176 * Converts the class name portion of a type descriptor to human-readable 177 * "dotted" form. 178 * 179 * Returns a newly-allocated string. 180 */ 181 static char* descriptorClassToDot(const char* str) 182 { 183 const char* lastSlash; 184 char* newStr; 185 char* cp; 186 187 /* reduce to just the class name, trimming trailing ';' */ 188 lastSlash = strrchr(str, '/'); 189 if (lastSlash == NULL) 190 lastSlash = str + 1; /* start past 'L' */ 191 else 192 lastSlash++; /* start past '/' */ 193 194 newStr = strdup(lastSlash); 195 newStr[strlen(lastSlash)-1] = '\0'; 196 for (cp = newStr; *cp != '\0'; cp++) { 197 if (*cp == '$') 198 *cp = '.'; 199 } 200 201 return newStr; 202 } 203 204 /* 205 * Returns a quoted string representing the boolean value. 206 */ 207 static const char* quotedBool(bool val) 208 { 209 if (val) 210 return "\"true\""; 211 else 212 return "\"false\""; 213 } 214 215 static const char* quotedVisibility(u4 accessFlags) 216 { 217 if ((accessFlags & ACC_PUBLIC) != 0) 218 return "\"public\""; 219 else if ((accessFlags & ACC_PROTECTED) != 0) 220 return "\"protected\""; 221 else if ((accessFlags & ACC_PRIVATE) != 0) 222 return "\"private\""; 223 else 224 return "\"package\""; 225 } 226 227 /* 228 * Count the number of '1' bits in a word. 229 */ 230 static int countOnes(u4 val) 231 { 232 int count = 0; 233 234 val = val - ((val >> 1) & 0x55555555); 235 val = (val & 0x33333333) + ((val >> 2) & 0x33333333); 236 count = (((val + (val >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24; 237 238 return count; 239 } 240 241 /* 242 * Flag for use with createAccessFlagStr(). 243 */ 244 enum AccessFor { 245 kAccessForClass = 0, kAccessForMethod = 1, kAccessForField = 2, 246 kAccessForMAX 247 }; 248 249 /* 250 * Create a new string with human-readable access flags. 251 * 252 * In the base language the access_flags fields are type u2; in Dalvik 253 * they're u4. 254 */ 255 static char* createAccessFlagStr(u4 flags, AccessFor forWhat) 256 { 257 #define NUM_FLAGS 18 258 static const char* kAccessStrings[kAccessForMAX][NUM_FLAGS] = { 259 { 260 /* class, inner class */ 261 "PUBLIC", /* 0x0001 */ 262 "PRIVATE", /* 0x0002 */ 263 "PROTECTED", /* 0x0004 */ 264 "STATIC", /* 0x0008 */ 265 "FINAL", /* 0x0010 */ 266 "?", /* 0x0020 */ 267 "?", /* 0x0040 */ 268 "?", /* 0x0080 */ 269 "?", /* 0x0100 */ 270 "INTERFACE", /* 0x0200 */ 271 "ABSTRACT", /* 0x0400 */ 272 "?", /* 0x0800 */ 273 "SYNTHETIC", /* 0x1000 */ 274 "ANNOTATION", /* 0x2000 */ 275 "ENUM", /* 0x4000 */ 276 "?", /* 0x8000 */ 277 "VERIFIED", /* 0x10000 */ 278 "OPTIMIZED", /* 0x20000 */ 279 }, 280 { 281 /* method */ 282 "PUBLIC", /* 0x0001 */ 283 "PRIVATE", /* 0x0002 */ 284 "PROTECTED", /* 0x0004 */ 285 "STATIC", /* 0x0008 */ 286 "FINAL", /* 0x0010 */ 287 "SYNCHRONIZED", /* 0x0020 */ 288 "BRIDGE", /* 0x0040 */ 289 "VARARGS", /* 0x0080 */ 290 "NATIVE", /* 0x0100 */ 291 "?", /* 0x0200 */ 292 "ABSTRACT", /* 0x0400 */ 293 "STRICT", /* 0x0800 */ 294 "SYNTHETIC", /* 0x1000 */ 295 "?", /* 0x2000 */ 296 "?", /* 0x4000 */ 297 "MIRANDA", /* 0x8000 */ 298 "CONSTRUCTOR", /* 0x10000 */ 299 "DECLARED_SYNCHRONIZED", /* 0x20000 */ 300 }, 301 { 302 /* field */ 303 "PUBLIC", /* 0x0001 */ 304 "PRIVATE", /* 0x0002 */ 305 "PROTECTED", /* 0x0004 */ 306 "STATIC", /* 0x0008 */ 307 "FINAL", /* 0x0010 */ 308 "?", /* 0x0020 */ 309 "VOLATILE", /* 0x0040 */ 310 "TRANSIENT", /* 0x0080 */ 311 "?", /* 0x0100 */ 312 "?", /* 0x0200 */ 313 "?", /* 0x0400 */ 314 "?", /* 0x0800 */ 315 "SYNTHETIC", /* 0x1000 */ 316 "?", /* 0x2000 */ 317 "ENUM", /* 0x4000 */ 318 "?", /* 0x8000 */ 319 "?", /* 0x10000 */ 320 "?", /* 0x20000 */ 321 }, 322 }; 323 const int kLongest = 21; /* strlen of longest string above */ 324 int i, count; 325 char* str; 326 char* cp; 327 328 /* 329 * Allocate enough storage to hold the expected number of strings, 330 * plus a space between each. We over-allocate, using the longest 331 * string above as the base metric. 332 */ 333 count = countOnes(flags); 334 cp = str = (char*) malloc(count * (kLongest+1) +1); 335 336 for (i = 0; i < NUM_FLAGS; i++) { 337 if (flags & 0x01) { 338 const char* accessStr = kAccessStrings[forWhat][i]; 339 int len = strlen(accessStr); 340 if (cp != str) 341 *cp++ = ' '; 342 343 memcpy(cp, accessStr, len); 344 cp += len; 345 } 346 flags >>= 1; 347 } 348 *cp = '\0'; 349 350 return str; 351 } 352 353 354 /* 355 * Copy character data from "data" to "out", converting non-ASCII values 356 * to printf format chars or an ASCII filler ('.' or '?'). 357 * 358 * The output buffer must be able to hold (2*len)+1 bytes. The result is 359 * NUL-terminated. 360 */ 361 static void asciify(char* out, const unsigned char* data, size_t len) 362 { 363 while (len--) { 364 if (*data < 0x20) { 365 /* could do more here, but we don't need them yet */ 366 switch (*data) { 367 case '\0': 368 *out++ = '\\'; 369 *out++ = '0'; 370 break; 371 case '\n': 372 *out++ = '\\'; 373 *out++ = 'n'; 374 break; 375 default: 376 *out++ = '.'; 377 break; 378 } 379 } else if (*data >= 0x80) { 380 *out++ = '?'; 381 } else { 382 *out++ = *data; 383 } 384 data++; 385 } 386 *out = '\0'; 387 } 388 389 /* 390 * Dump the file header. 391 */ 392 void dumpFileHeader(const DexFile* pDexFile) 393 { 394 const DexOptHeader* pOptHeader = pDexFile->pOptHeader; 395 const DexHeader* pHeader = pDexFile->pHeader; 396 char sanitized[sizeof(pHeader->magic)*2 +1]; 397 398 assert(sizeof(pHeader->magic) == sizeof(pOptHeader->magic)); 399 400 if (pOptHeader != NULL) { 401 printf("Optimized DEX file header:\n"); 402 403 asciify(sanitized, pOptHeader->magic, sizeof(pOptHeader->magic)); 404 printf("magic : '%s'\n", sanitized); 405 printf("dex_offset : %d (0x%06x)\n", 406 pOptHeader->dexOffset, pOptHeader->dexOffset); 407 printf("dex_length : %d\n", pOptHeader->dexLength); 408 printf("deps_offset : %d (0x%06x)\n", 409 pOptHeader->depsOffset, pOptHeader->depsOffset); 410 printf("deps_length : %d\n", pOptHeader->depsLength); 411 printf("opt_offset : %d (0x%06x)\n", 412 pOptHeader->optOffset, pOptHeader->optOffset); 413 printf("opt_length : %d\n", pOptHeader->optLength); 414 printf("flags : %08x\n", pOptHeader->flags); 415 printf("checksum : %08x\n", pOptHeader->checksum); 416 printf("\n"); 417 } 418 419 printf("DEX file header:\n"); 420 asciify(sanitized, pHeader->magic, sizeof(pHeader->magic)); 421 printf("magic : '%s'\n", sanitized); 422 printf("checksum : %08x\n", pHeader->checksum); 423 printf("signature : %02x%02x...%02x%02x\n", 424 pHeader->signature[0], pHeader->signature[1], 425 pHeader->signature[kSHA1DigestLen-2], 426 pHeader->signature[kSHA1DigestLen-1]); 427 printf("file_size : %d\n", pHeader->fileSize); 428 printf("header_size : %d\n", pHeader->headerSize); 429 printf("link_size : %d\n", pHeader->linkSize); 430 printf("link_off : %d (0x%06x)\n", 431 pHeader->linkOff, pHeader->linkOff); 432 printf("string_ids_size : %d\n", pHeader->stringIdsSize); 433 printf("string_ids_off : %d (0x%06x)\n", 434 pHeader->stringIdsOff, pHeader->stringIdsOff); 435 printf("type_ids_size : %d\n", pHeader->typeIdsSize); 436 printf("type_ids_off : %d (0x%06x)\n", 437 pHeader->typeIdsOff, pHeader->typeIdsOff); 438 printf("proto_ids_size : %d\n", pHeader->protoIdsSize); 439 printf("proto_ids_off : %d (0x%06x)\n", 440 pHeader->protoIdsOff, pHeader->protoIdsOff); 441 printf("field_ids_size : %d\n", pHeader->fieldIdsSize); 442 printf("field_ids_off : %d (0x%06x)\n", 443 pHeader->fieldIdsOff, pHeader->fieldIdsOff); 444 printf("method_ids_size : %d\n", pHeader->methodIdsSize); 445 printf("method_ids_off : %d (0x%06x)\n", 446 pHeader->methodIdsOff, pHeader->methodIdsOff); 447 printf("class_defs_size : %d\n", pHeader->classDefsSize); 448 printf("class_defs_off : %d (0x%06x)\n", 449 pHeader->classDefsOff, pHeader->classDefsOff); 450 printf("data_size : %d\n", pHeader->dataSize); 451 printf("data_off : %d (0x%06x)\n", 452 pHeader->dataOff, pHeader->dataOff); 453 printf("\n"); 454 } 455 456 /* 457 * Dump the "table of contents" for the opt area. 458 */ 459 void dumpOptDirectory(const DexFile* pDexFile) 460 { 461 const DexOptHeader* pOptHeader = pDexFile->pOptHeader; 462 if (pOptHeader == NULL) 463 return; 464 465 printf("OPT section contents:\n"); 466 467 const u4* pOpt = (const u4*) ((u1*) pOptHeader + pOptHeader->optOffset); 468 469 if (*pOpt == 0) { 470 printf("(1.0 format, only class lookup table is present)\n\n"); 471 return; 472 } 473 474 /* 475 * The "opt" section is in "chunk" format: a 32-bit identifier, a 32-bit 476 * length, then the data. Chunks start on 64-bit boundaries. 477 */ 478 while (*pOpt != kDexChunkEnd) { 479 const char* verboseStr; 480 481 u4 size = *(pOpt+1); 482 483 switch (*pOpt) { 484 case kDexChunkClassLookup: 485 verboseStr = "class lookup hash table"; 486 break; 487 case kDexChunkRegisterMaps: 488 verboseStr = "register maps"; 489 break; 490 default: 491 verboseStr = "(unknown chunk type)"; 492 break; 493 } 494 495 printf("Chunk %08x (%c%c%c%c) - %s (%d bytes)\n", *pOpt, 496 *pOpt >> 24, (char)(*pOpt >> 16), (char)(*pOpt >> 8), (char)*pOpt, 497 verboseStr, size); 498 499 size = (size + 8 + 7) & ~7; 500 pOpt += size / sizeof(u4); 501 } 502 printf("\n"); 503 } 504 505 /* 506 * Dump a class_def_item. 507 */ 508 void dumpClassDef(DexFile* pDexFile, int idx) 509 { 510 const DexClassDef* pClassDef; 511 const u1* pEncodedData; 512 DexClassData* pClassData; 513 514 pClassDef = dexGetClassDef(pDexFile, idx); 515 pEncodedData = dexGetClassData(pDexFile, pClassDef); 516 pClassData = dexReadAndVerifyClassData(&pEncodedData, NULL); 517 518 if (pClassData == NULL) { 519 fprintf(stderr, "Trouble reading class data\n"); 520 return; 521 } 522 523 printf("Class #%d header:\n", idx); 524 printf("class_idx : %d\n", pClassDef->classIdx); 525 printf("access_flags : %d (0x%04x)\n", 526 pClassDef->accessFlags, pClassDef->accessFlags); 527 printf("superclass_idx : %d\n", pClassDef->superclassIdx); 528 printf("interfaces_off : %d (0x%06x)\n", 529 pClassDef->interfacesOff, pClassDef->interfacesOff); 530 printf("source_file_idx : %d\n", pClassDef->sourceFileIdx); 531 printf("annotations_off : %d (0x%06x)\n", 532 pClassDef->annotationsOff, pClassDef->annotationsOff); 533 printf("class_data_off : %d (0x%06x)\n", 534 pClassDef->classDataOff, pClassDef->classDataOff); 535 printf("static_fields_size : %d\n", pClassData->header.staticFieldsSize); 536 printf("instance_fields_size: %d\n", 537 pClassData->header.instanceFieldsSize); 538 printf("direct_methods_size : %d\n", pClassData->header.directMethodsSize); 539 printf("virtual_methods_size: %d\n", 540 pClassData->header.virtualMethodsSize); 541 printf("\n"); 542 543 free(pClassData); 544 } 545 546 /* 547 * Dump an interface that a class declares to implement. 548 */ 549 void dumpInterface(const DexFile* pDexFile, const DexTypeItem* pTypeItem, 550 int i) 551 { 552 const char* interfaceName = 553 dexStringByTypeIdx(pDexFile, pTypeItem->typeIdx); 554 555 if (gOptions.outputFormat == OUTPUT_PLAIN) { 556 printf(" #%d : '%s'\n", i, interfaceName); 557 } else { 558 char* dotted = descriptorToDot(interfaceName); 559 printf("<implements name=\"%s\">\n</implements>\n", dotted); 560 free(dotted); 561 } 562 } 563 564 /* 565 * Dump the catches table associated with the code. 566 */ 567 void dumpCatches(DexFile* pDexFile, const DexCode* pCode) 568 { 569 u4 triesSize = pCode->triesSize; 570 571 if (triesSize == 0) { 572 printf(" catches : (none)\n"); 573 return; 574 } 575 576 printf(" catches : %d\n", triesSize); 577 578 const DexTry* pTries = dexGetTries(pCode); 579 u4 i; 580 581 for (i = 0; i < triesSize; i++) { 582 const DexTry* pTry = &pTries[i]; 583 u4 start = pTry->startAddr; 584 u4 end = start + pTry->insnCount; 585 DexCatchIterator iterator; 586 587 printf(" 0x%04x - 0x%04x\n", start, end); 588 589 dexCatchIteratorInit(&iterator, pCode, pTry->handlerOff); 590 591 for (;;) { 592 DexCatchHandler* handler = dexCatchIteratorNext(&iterator); 593 const char* descriptor; 594 595 if (handler == NULL) { 596 break; 597 } 598 599 descriptor = (handler->typeIdx == kDexNoIndex) ? "<any>" : 600 dexStringByTypeIdx(pDexFile, handler->typeIdx); 601 602 printf(" %s -> 0x%04x\n", descriptor, 603 handler->address); 604 } 605 } 606 } 607 608 static int dumpPositionsCb(void *cnxt, u4 address, u4 lineNum) 609 { 610 printf(" 0x%04x line=%d\n", address, lineNum); 611 return 0; 612 } 613 614 /* 615 * Dump the positions list. 616 */ 617 void dumpPositions(DexFile* pDexFile, const DexCode* pCode, 618 const DexMethod *pDexMethod) 619 { 620 printf(" positions : \n"); 621 const DexMethodId *pMethodId 622 = dexGetMethodId(pDexFile, pDexMethod->methodIdx); 623 const char *classDescriptor 624 = dexStringByTypeIdx(pDexFile, pMethodId->classIdx); 625 626 dexDecodeDebugInfo(pDexFile, pCode, classDescriptor, pMethodId->protoIdx, 627 pDexMethod->accessFlags, dumpPositionsCb, NULL, NULL); 628 } 629 630 static void dumpLocalsCb(void *cnxt, u2 reg, u4 startAddress, 631 u4 endAddress, const char *name, const char *descriptor, 632 const char *signature) 633 { 634 printf(" 0x%04x - 0x%04x reg=%d %s %s %s\n", 635 startAddress, endAddress, reg, name, descriptor, 636 signature); 637 } 638 639 /* 640 * Dump the locals list. 641 */ 642 void dumpLocals(DexFile* pDexFile, const DexCode* pCode, 643 const DexMethod *pDexMethod) 644 { 645 printf(" locals : \n"); 646 647 const DexMethodId *pMethodId 648 = dexGetMethodId(pDexFile, pDexMethod->methodIdx); 649 const char *classDescriptor 650 = dexStringByTypeIdx(pDexFile, pMethodId->classIdx); 651 652 dexDecodeDebugInfo(pDexFile, pCode, classDescriptor, pMethodId->protoIdx, 653 pDexMethod->accessFlags, NULL, dumpLocalsCb, NULL); 654 } 655 656 /* 657 * Get information about a method. 658 */ 659 bool getMethodInfo(DexFile* pDexFile, u4 methodIdx, FieldMethodInfo* pMethInfo) 660 { 661 const DexMethodId* pMethodId; 662 663 if (methodIdx >= pDexFile->pHeader->methodIdsSize) 664 return false; 665 666 pMethodId = dexGetMethodId(pDexFile, methodIdx); 667 pMethInfo->name = dexStringById(pDexFile, pMethodId->nameIdx); 668 pMethInfo->signature = dexCopyDescriptorFromMethodId(pDexFile, pMethodId); 669 670 pMethInfo->classDescriptor = 671 dexStringByTypeIdx(pDexFile, pMethodId->classIdx); 672 return true; 673 } 674 675 /* 676 * Get information about a field. 677 */ 678 bool getFieldInfo(DexFile* pDexFile, u4 fieldIdx, FieldMethodInfo* pFieldInfo) 679 { 680 const DexFieldId* pFieldId; 681 682 if (fieldIdx >= pDexFile->pHeader->fieldIdsSize) 683 return false; 684 685 pFieldId = dexGetFieldId(pDexFile, fieldIdx); 686 pFieldInfo->name = dexStringById(pDexFile, pFieldId->nameIdx); 687 pFieldInfo->signature = dexStringByTypeIdx(pDexFile, pFieldId->typeIdx); 688 pFieldInfo->classDescriptor = 689 dexStringByTypeIdx(pDexFile, pFieldId->classIdx); 690 return true; 691 } 692 693 694 /* 695 * Look up a class' descriptor. 696 */ 697 const char* getClassDescriptor(DexFile* pDexFile, u4 classIdx) 698 { 699 return dexStringByTypeIdx(pDexFile, classIdx); 700 } 701 702 /* 703 * Helper for dumpInstruction(), which builds the string 704 * representation for the index in the given instruction. This will 705 * first try to use the given buffer, but if the result won't fit, 706 * then this will allocate a new buffer to hold the result. A pointer 707 * to the buffer which holds the full result is always returned, and 708 * this can be compared with the one passed in, to see if the result 709 * needs to be free()d. 710 */ 711 static char* indexString(DexFile* pDexFile, 712 const DecodedInstruction* pDecInsn, char* buf, size_t bufSize) 713 { 714 int outSize; 715 u4 index; 716 u4 width; 717 718 /* TODO: Make the index *always* be in field B, to simplify this code. */ 719 switch (dexGetFormatFromOpcode(pDecInsn->opcode)) { 720 case kFmt20bc: 721 case kFmt21c: 722 case kFmt35c: 723 case kFmt35ms: 724 case kFmt3rc: 725 case kFmt3rms: 726 case kFmt35mi: 727 case kFmt3rmi: 728 index = pDecInsn->vB; 729 width = 4; 730 break; 731 case kFmt31c: 732 index = pDecInsn->vB; 733 width = 8; 734 break; 735 case kFmt22c: 736 case kFmt22cs: 737 index = pDecInsn->vC; 738 width = 4; 739 break; 740 default: 741 index = 0; 742 width = 4; 743 break; 744 } 745 746 switch (pDecInsn->indexType) { 747 case kIndexUnknown: 748 /* 749 * This function shouldn't ever get called for this type, but do 750 * something sensible here, just to help with debugging. 751 */ 752 outSize = snprintf(buf, bufSize, "<unknown-index>"); 753 break; 754 case kIndexNone: 755 /* 756 * This function shouldn't ever get called for this type, but do 757 * something sensible here, just to help with debugging. 758 */ 759 outSize = snprintf(buf, bufSize, "<no-index>"); 760 break; 761 case kIndexVaries: 762 /* 763 * This one should never show up in a dexdump, so no need to try 764 * to get fancy here. 765 */ 766 outSize = snprintf(buf, bufSize, "<index-varies> // thing@%0*x", 767 width, index); 768 break; 769 case kIndexTypeRef: 770 if (index < pDexFile->pHeader->typeIdsSize) { 771 outSize = snprintf(buf, bufSize, "%s // type@%0*x", 772 getClassDescriptor(pDexFile, index), width, index); 773 } else { 774 outSize = snprintf(buf, bufSize, "<type?> // type@%0*x", width, index); 775 } 776 break; 777 case kIndexStringRef: 778 if (index < pDexFile->pHeader->stringIdsSize) { 779 outSize = snprintf(buf, bufSize, "\"%s\" // string@%0*x", 780 dexStringById(pDexFile, index), width, index); 781 } else { 782 outSize = snprintf(buf, bufSize, "<string?> // string@%0*x", 783 width, index); 784 } 785 break; 786 case kIndexMethodRef: 787 { 788 FieldMethodInfo methInfo; 789 if (getMethodInfo(pDexFile, index, &methInfo)) { 790 outSize = snprintf(buf, bufSize, "%s.%s:%s // method@%0*x", 791 methInfo.classDescriptor, methInfo.name, 792 methInfo.signature, width, index); 793 free((void *) methInfo.signature); 794 } else { 795 outSize = snprintf(buf, bufSize, "<method?> // method@%0*x", 796 width, index); 797 } 798 } 799 break; 800 case kIndexFieldRef: 801 { 802 FieldMethodInfo fieldInfo; 803 if (getFieldInfo(pDexFile, index, &fieldInfo)) { 804 outSize = snprintf(buf, bufSize, "%s.%s:%s // field@%0*x", 805 fieldInfo.classDescriptor, fieldInfo.name, 806 fieldInfo.signature, width, index); 807 } else { 808 outSize = snprintf(buf, bufSize, "<field?> // field@%0*x", 809 width, index); 810 } 811 } 812 break; 813 case kIndexInlineMethod: 814 outSize = snprintf(buf, bufSize, "[%0*x] // inline #%0*x", 815 width, index, width, index); 816 break; 817 case kIndexVtableOffset: 818 outSize = snprintf(buf, bufSize, "[%0*x] // vtable #%0*x", 819 width, index, width, index); 820 break; 821 case kIndexFieldOffset: 822 outSize = snprintf(buf, bufSize, "[obj+%0*x]", width, index); 823 break; 824 default: 825 outSize = snprintf(buf, bufSize, "<?>"); 826 break; 827 } 828 829 if (outSize >= (int) bufSize) { 830 /* 831 * The buffer wasn't big enough; allocate and retry. Note: 832 * snprintf() doesn't count the '\0' as part of its returned 833 * size, so we add explicit space for it here. 834 */ 835 outSize++; 836 buf = (char*)malloc(outSize); 837 if (buf == NULL) { 838 return NULL; 839 } 840 return indexString(pDexFile, pDecInsn, buf, outSize); 841 } else { 842 return buf; 843 } 844 } 845 846 /* 847 * Dump a single instruction. 848 */ 849 void dumpInstruction(DexFile* pDexFile, const DexCode* pCode, int insnIdx, 850 int insnWidth, const DecodedInstruction* pDecInsn) 851 { 852 char indexBufChars[200]; 853 char *indexBuf = indexBufChars; 854 const u2* insns = pCode->insns; 855 int i; 856 857 printf("%06x:", ((u1*)insns - pDexFile->baseAddr) + insnIdx*2); 858 for (i = 0; i < 8; i++) { 859 if (i < insnWidth) { 860 if (i == 7) { 861 printf(" ... "); 862 } else { 863 /* print 16-bit value in little-endian order */ 864 const u1* bytePtr = (const u1*) &insns[insnIdx+i]; 865 printf(" %02x%02x", bytePtr[0], bytePtr[1]); 866 } 867 } else { 868 fputs(" ", stdout); 869 } 870 } 871 872 if (pDecInsn->opcode == OP_NOP) { 873 u2 instr = get2LE((const u1*) &insns[insnIdx]); 874 if (instr == kPackedSwitchSignature) { 875 printf("|%04x: packed-switch-data (%d units)", 876 insnIdx, insnWidth); 877 } else if (instr == kSparseSwitchSignature) { 878 printf("|%04x: sparse-switch-data (%d units)", 879 insnIdx, insnWidth); 880 } else if (instr == kArrayDataSignature) { 881 printf("|%04x: array-data (%d units)", 882 insnIdx, insnWidth); 883 } else { 884 printf("|%04x: nop // spacer", insnIdx); 885 } 886 } else { 887 printf("|%04x: %s", insnIdx, dexGetOpcodeName(pDecInsn->opcode)); 888 } 889 890 if (pDecInsn->indexType != kIndexNone) { 891 indexBuf = indexString(pDexFile, pDecInsn, 892 indexBufChars, sizeof(indexBufChars)); 893 } 894 895 switch (dexGetFormatFromOpcode(pDecInsn->opcode)) { 896 case kFmt10x: // op 897 break; 898 case kFmt12x: // op vA, vB 899 printf(" v%d, v%d", pDecInsn->vA, pDecInsn->vB); 900 break; 901 case kFmt11n: // op vA, #+B 902 printf(" v%d, #int %d // #%x", 903 pDecInsn->vA, (s4)pDecInsn->vB, (u1)pDecInsn->vB); 904 break; 905 case kFmt11x: // op vAA 906 printf(" v%d", pDecInsn->vA); 907 break; 908 case kFmt10t: // op +AA 909 case kFmt20t: // op +AAAA 910 { 911 s4 targ = (s4) pDecInsn->vA; 912 printf(" %04x // %c%04x", 913 insnIdx + targ, 914 (targ < 0) ? '-' : '+', 915 (targ < 0) ? -targ : targ); 916 } 917 break; 918 case kFmt22x: // op vAA, vBBBB 919 printf(" v%d, v%d", pDecInsn->vA, pDecInsn->vB); 920 break; 921 case kFmt21t: // op vAA, +BBBB 922 { 923 s4 targ = (s4) pDecInsn->vB; 924 printf(" v%d, %04x // %c%04x", pDecInsn->vA, 925 insnIdx + targ, 926 (targ < 0) ? '-' : '+', 927 (targ < 0) ? -targ : targ); 928 } 929 break; 930 case kFmt21s: // op vAA, #+BBBB 931 printf(" v%d, #int %d // #%x", 932 pDecInsn->vA, (s4)pDecInsn->vB, (u2)pDecInsn->vB); 933 break; 934 case kFmt21h: // op vAA, #+BBBB0000[00000000] 935 // The printed format varies a bit based on the actual opcode. 936 if (pDecInsn->opcode == OP_CONST_HIGH16) { 937 s4 value = pDecInsn->vB << 16; 938 printf(" v%d, #int %d // #%x", 939 pDecInsn->vA, value, (u2)pDecInsn->vB); 940 } else { 941 s8 value = ((s8) pDecInsn->vB) << 48; 942 printf(" v%d, #long %lld // #%x", 943 pDecInsn->vA, value, (u2)pDecInsn->vB); 944 } 945 break; 946 case kFmt21c: // op vAA, thing@BBBB 947 case kFmt31c: // op vAA, thing@BBBBBBBB 948 printf(" v%d, %s", pDecInsn->vA, indexBuf); 949 break; 950 case kFmt23x: // op vAA, vBB, vCC 951 printf(" v%d, v%d, v%d", pDecInsn->vA, pDecInsn->vB, pDecInsn->vC); 952 break; 953 case kFmt22b: // op vAA, vBB, #+CC 954 printf(" v%d, v%d, #int %d // #%02x", 955 pDecInsn->vA, pDecInsn->vB, (s4)pDecInsn->vC, (u1)pDecInsn->vC); 956 break; 957 case kFmt22t: // op vA, vB, +CCCC 958 { 959 s4 targ = (s4) pDecInsn->vC; 960 printf(" v%d, v%d, %04x // %c%04x", pDecInsn->vA, pDecInsn->vB, 961 insnIdx + targ, 962 (targ < 0) ? '-' : '+', 963 (targ < 0) ? -targ : targ); 964 } 965 break; 966 case kFmt22s: // op vA, vB, #+CCCC 967 printf(" v%d, v%d, #int %d // #%04x", 968 pDecInsn->vA, pDecInsn->vB, (s4)pDecInsn->vC, (u2)pDecInsn->vC); 969 break; 970 case kFmt22c: // op vA, vB, thing@CCCC 971 case kFmt22cs: // [opt] op vA, vB, field offset CCCC 972 printf(" v%d, v%d, %s", pDecInsn->vA, pDecInsn->vB, indexBuf); 973 break; 974 case kFmt30t: 975 printf(" #%08x", pDecInsn->vA); 976 break; 977 case kFmt31i: // op vAA, #+BBBBBBBB 978 { 979 /* this is often, but not always, a float */ 980 union { 981 float f; 982 u4 i; 983 } conv; 984 conv.i = pDecInsn->vB; 985 printf(" v%d, #float %f // #%08x", 986 pDecInsn->vA, conv.f, pDecInsn->vB); 987 } 988 break; 989 case kFmt31t: // op vAA, offset +BBBBBBBB 990 printf(" v%d, %08x // +%08x", 991 pDecInsn->vA, insnIdx + pDecInsn->vB, pDecInsn->vB); 992 break; 993 case kFmt32x: // op vAAAA, vBBBB 994 printf(" v%d, v%d", pDecInsn->vA, pDecInsn->vB); 995 break; 996 case kFmt35c: // op {vC, vD, vE, vF, vG}, thing@BBBB 997 case kFmt35ms: // [opt] invoke-virtual+super 998 case kFmt35mi: // [opt] inline invoke 999 { 1000 fputs(" {", stdout); 1001 for (i = 0; i < (int) pDecInsn->vA; i++) { 1002 if (i == 0) 1003 printf("v%d", pDecInsn->arg[i]); 1004 else 1005 printf(", v%d", pDecInsn->arg[i]); 1006 } 1007 printf("}, %s", indexBuf); 1008 } 1009 break; 1010 case kFmt3rc: // op {vCCCC .. v(CCCC+AA-1)}, thing@BBBB 1011 case kFmt3rms: // [opt] invoke-virtual+super/range 1012 case kFmt3rmi: // [opt] execute-inline/range 1013 { 1014 /* 1015 * This doesn't match the "dx" output when some of the args are 1016 * 64-bit values -- dx only shows the first register. 1017 */ 1018 fputs(" {", stdout); 1019 for (i = 0; i < (int) pDecInsn->vA; i++) { 1020 if (i == 0) 1021 printf("v%d", pDecInsn->vC + i); 1022 else 1023 printf(", v%d", pDecInsn->vC + i); 1024 } 1025 printf("}, %s", indexBuf); 1026 } 1027 break; 1028 case kFmt51l: // op vAA, #+BBBBBBBBBBBBBBBB 1029 { 1030 /* this is often, but not always, a double */ 1031 union { 1032 double d; 1033 u8 j; 1034 } conv; 1035 conv.j = pDecInsn->vB_wide; 1036 printf(" v%d, #double %f // #%016llx", 1037 pDecInsn->vA, conv.d, pDecInsn->vB_wide); 1038 } 1039 break; 1040 case kFmt00x: // unknown op or breakpoint 1041 break; 1042 default: 1043 printf(" ???"); 1044 break; 1045 } 1046 1047 putchar('\n'); 1048 1049 if (indexBuf != indexBufChars) { 1050 free(indexBuf); 1051 } 1052 } 1053 1054 /* 1055 * Dump a bytecode disassembly. 1056 */ 1057 void dumpBytecodes(DexFile* pDexFile, const DexMethod* pDexMethod) 1058 { 1059 const DexCode* pCode = dexGetCode(pDexFile, pDexMethod); 1060 const u2* insns; 1061 int insnIdx; 1062 FieldMethodInfo methInfo; 1063 int startAddr; 1064 char* className = NULL; 1065 1066 assert(pCode->insnsSize > 0); 1067 insns = pCode->insns; 1068 1069 getMethodInfo(pDexFile, pDexMethod->methodIdx, &methInfo); 1070 startAddr = ((u1*)pCode - pDexFile->baseAddr); 1071 className = descriptorToDot(methInfo.classDescriptor); 1072 1073 printf("%06x: |[%06x] %s.%s:%s\n", 1074 startAddr, startAddr, 1075 className, methInfo.name, methInfo.signature); 1076 free((void *) methInfo.signature); 1077 1078 insnIdx = 0; 1079 while (insnIdx < (int) pCode->insnsSize) { 1080 int insnWidth; 1081 DecodedInstruction decInsn; 1082 u2 instr; 1083 1084 /* 1085 * Note: This code parallels the function 1086 * dexGetWidthFromInstruction() in InstrUtils.c, but this version 1087 * can deal with data in either endianness. 1088 * 1089 * TODO: Figure out if this really matters, and possibly change 1090 * this to just use dexGetWidthFromInstruction(). 1091 */ 1092 instr = get2LE((const u1*)insns); 1093 if (instr == kPackedSwitchSignature) { 1094 insnWidth = 4 + get2LE((const u1*)(insns+1)) * 2; 1095 } else if (instr == kSparseSwitchSignature) { 1096 insnWidth = 2 + get2LE((const u1*)(insns+1)) * 4; 1097 } else if (instr == kArrayDataSignature) { 1098 int width = get2LE((const u1*)(insns+1)); 1099 int size = get2LE((const u1*)(insns+2)) | 1100 (get2LE((const u1*)(insns+3))<<16); 1101 // The plus 1 is to round up for odd size and width. 1102 insnWidth = 4 + ((size * width) + 1) / 2; 1103 } else { 1104 Opcode opcode = dexOpcodeFromCodeUnit(instr); 1105 insnWidth = dexGetWidthFromOpcode(opcode); 1106 if (insnWidth == 0) { 1107 fprintf(stderr, 1108 "GLITCH: zero-width instruction at idx=0x%04x\n", insnIdx); 1109 break; 1110 } 1111 } 1112 1113 dexDecodeInstruction(insns, &decInsn); 1114 dumpInstruction(pDexFile, pCode, insnIdx, insnWidth, &decInsn); 1115 1116 insns += insnWidth; 1117 insnIdx += insnWidth; 1118 } 1119 1120 free(className); 1121 } 1122 1123 /* 1124 * Dump a "code" struct. 1125 */ 1126 void dumpCode(DexFile* pDexFile, const DexMethod* pDexMethod) 1127 { 1128 const DexCode* pCode = dexGetCode(pDexFile, pDexMethod); 1129 1130 printf(" registers : %d\n", pCode->registersSize); 1131 printf(" ins : %d\n", pCode->insSize); 1132 printf(" outs : %d\n", pCode->outsSize); 1133 printf(" insns size : %d 16-bit code units\n", pCode->insnsSize); 1134 1135 if (gOptions.disassemble) 1136 dumpBytecodes(pDexFile, pDexMethod); 1137 1138 dumpCatches(pDexFile, pCode); 1139 /* both of these are encoded in debug info */ 1140 dumpPositions(pDexFile, pCode, pDexMethod); 1141 dumpLocals(pDexFile, pCode, pDexMethod); 1142 } 1143 1144 /* 1145 * Dump a method. 1146 */ 1147 void dumpMethod(DexFile* pDexFile, const DexMethod* pDexMethod, int i) 1148 { 1149 const DexMethodId* pMethodId; 1150 const char* backDescriptor; 1151 const char* name; 1152 char* typeDescriptor = NULL; 1153 char* accessStr = NULL; 1154 1155 if (gOptions.exportsOnly && 1156 (pDexMethod->accessFlags & (ACC_PUBLIC | ACC_PROTECTED)) == 0) 1157 { 1158 return; 1159 } 1160 1161 pMethodId = dexGetMethodId(pDexFile, pDexMethod->methodIdx); 1162 name = dexStringById(pDexFile, pMethodId->nameIdx); 1163 typeDescriptor = dexCopyDescriptorFromMethodId(pDexFile, pMethodId); 1164 1165 backDescriptor = dexStringByTypeIdx(pDexFile, pMethodId->classIdx); 1166 1167 accessStr = createAccessFlagStr(pDexMethod->accessFlags, 1168 kAccessForMethod); 1169 1170 if (gOptions.outputFormat == OUTPUT_PLAIN) { 1171 printf(" #%d : (in %s)\n", i, backDescriptor); 1172 printf(" name : '%s'\n", name); 1173 printf(" type : '%s'\n", typeDescriptor); 1174 printf(" access : 0x%04x (%s)\n", 1175 pDexMethod->accessFlags, accessStr); 1176 1177 if (pDexMethod->codeOff == 0) { 1178 printf(" code : (none)\n"); 1179 } else { 1180 printf(" code -\n"); 1181 dumpCode(pDexFile, pDexMethod); 1182 } 1183 1184 if (gOptions.disassemble) 1185 putchar('\n'); 1186 } else if (gOptions.outputFormat == OUTPUT_XML) { 1187 bool constructor = (name[0] == '<'); 1188 1189 if (constructor) { 1190 char* tmp; 1191 1192 tmp = descriptorClassToDot(backDescriptor); 1193 printf("<constructor name=\"%s\"\n", tmp); 1194 free(tmp); 1195 1196 tmp = descriptorToDot(backDescriptor); 1197 printf(" type=\"%s\"\n", tmp); 1198 free(tmp); 1199 } else { 1200 printf("<method name=\"%s\"\n", name); 1201 1202 const char* returnType = strrchr(typeDescriptor, ')'); 1203 if (returnType == NULL) { 1204 fprintf(stderr, "bad method type descriptor '%s'\n", 1205 typeDescriptor); 1206 goto bail; 1207 } 1208 1209 char* tmp = descriptorToDot(returnType+1); 1210 printf(" return=\"%s\"\n", tmp); 1211 free(tmp); 1212 1213 printf(" abstract=%s\n", 1214 quotedBool((pDexMethod->accessFlags & ACC_ABSTRACT) != 0)); 1215 printf(" native=%s\n", 1216 quotedBool((pDexMethod->accessFlags & ACC_NATIVE) != 0)); 1217 1218 bool isSync = 1219 (pDexMethod->accessFlags & ACC_SYNCHRONIZED) != 0 || 1220 (pDexMethod->accessFlags & ACC_DECLARED_SYNCHRONIZED) != 0; 1221 printf(" synchronized=%s\n", quotedBool(isSync)); 1222 } 1223 1224 printf(" static=%s\n", 1225 quotedBool((pDexMethod->accessFlags & ACC_STATIC) != 0)); 1226 printf(" final=%s\n", 1227 quotedBool((pDexMethod->accessFlags & ACC_FINAL) != 0)); 1228 // "deprecated=" not knowable w/o parsing annotations 1229 printf(" visibility=%s\n", 1230 quotedVisibility(pDexMethod->accessFlags)); 1231 1232 printf(">\n"); 1233 1234 /* 1235 * Parameters. 1236 */ 1237 if (typeDescriptor[0] != '(') { 1238 fprintf(stderr, "ERROR: bad descriptor '%s'\n", typeDescriptor); 1239 goto bail; 1240 } 1241 1242 char tmpBuf[strlen(typeDescriptor)+1]; /* more than big enough */ 1243 int argNum = 0; 1244 1245 const char* base = typeDescriptor+1; 1246 1247 while (*base != ')') { 1248 char* cp = tmpBuf; 1249 1250 while (*base == '[') 1251 *cp++ = *base++; 1252 1253 if (*base == 'L') { 1254 /* copy through ';' */ 1255 do { 1256 *cp = *base++; 1257 } while (*cp++ != ';'); 1258 } else { 1259 /* primitive char, copy it */ 1260 if (strchr("ZBCSIFJD", *base) == NULL) { 1261 fprintf(stderr, "ERROR: bad method signature '%s'\n", base); 1262 goto bail; 1263 } 1264 *cp++ = *base++; 1265 } 1266 1267 /* null terminate and display */ 1268 *cp++ = '\0'; 1269 1270 char* tmp = descriptorToDot(tmpBuf); 1271 printf("<parameter name=\"arg%d\" type=\"%s\">\n</parameter>\n", 1272 argNum++, tmp); 1273 free(tmp); 1274 } 1275 1276 if (constructor) 1277 printf("</constructor>\n"); 1278 else 1279 printf("</method>\n"); 1280 } 1281 1282 bail: 1283 free(typeDescriptor); 1284 free(accessStr); 1285 } 1286 1287 /* 1288 * Dump a static (class) field. 1289 */ 1290 void dumpSField(const DexFile* pDexFile, const DexField* pSField, int i) 1291 { 1292 const DexFieldId* pFieldId; 1293 const char* backDescriptor; 1294 const char* name; 1295 const char* typeDescriptor; 1296 char* accessStr; 1297 1298 if (gOptions.exportsOnly && 1299 (pSField->accessFlags & (ACC_PUBLIC | ACC_PROTECTED)) == 0) 1300 { 1301 return; 1302 } 1303 1304 pFieldId = dexGetFieldId(pDexFile, pSField->fieldIdx); 1305 name = dexStringById(pDexFile, pFieldId->nameIdx); 1306 typeDescriptor = dexStringByTypeIdx(pDexFile, pFieldId->typeIdx); 1307 backDescriptor = dexStringByTypeIdx(pDexFile, pFieldId->classIdx); 1308 1309 accessStr = createAccessFlagStr(pSField->accessFlags, kAccessForField); 1310 1311 if (gOptions.outputFormat == OUTPUT_PLAIN) { 1312 printf(" #%d : (in %s)\n", i, backDescriptor); 1313 printf(" name : '%s'\n", name); 1314 printf(" type : '%s'\n", typeDescriptor); 1315 printf(" access : 0x%04x (%s)\n", 1316 pSField->accessFlags, accessStr); 1317 } else if (gOptions.outputFormat == OUTPUT_XML) { 1318 char* tmp; 1319 1320 printf("<field name=\"%s\"\n", name); 1321 1322 tmp = descriptorToDot(typeDescriptor); 1323 printf(" type=\"%s\"\n", tmp); 1324 free(tmp); 1325 1326 printf(" transient=%s\n", 1327 quotedBool((pSField->accessFlags & ACC_TRANSIENT) != 0)); 1328 printf(" volatile=%s\n", 1329 quotedBool((pSField->accessFlags & ACC_VOLATILE) != 0)); 1330 // "value=" not knowable w/o parsing annotations 1331 printf(" static=%s\n", 1332 quotedBool((pSField->accessFlags & ACC_STATIC) != 0)); 1333 printf(" final=%s\n", 1334 quotedBool((pSField->accessFlags & ACC_FINAL) != 0)); 1335 // "deprecated=" not knowable w/o parsing annotations 1336 printf(" visibility=%s\n", 1337 quotedVisibility(pSField->accessFlags)); 1338 printf(">\n</field>\n"); 1339 } 1340 1341 free(accessStr); 1342 } 1343 1344 /* 1345 * Dump an instance field. 1346 */ 1347 void dumpIField(const DexFile* pDexFile, const DexField* pIField, int i) 1348 { 1349 dumpSField(pDexFile, pIField, i); 1350 } 1351 1352 /* 1353 * Dump the class. 1354 * 1355 * Note "idx" is a DexClassDef index, not a DexTypeId index. 1356 * 1357 * If "*pLastPackage" is NULL or does not match the current class' package, 1358 * the value will be replaced with a newly-allocated string. 1359 */ 1360 void dumpClass(DexFile* pDexFile, int idx, char** pLastPackage) 1361 { 1362 const DexTypeList* pInterfaces; 1363 const DexClassDef* pClassDef; 1364 DexClassData* pClassData = NULL; 1365 const u1* pEncodedData; 1366 const char* fileName; 1367 const char* classDescriptor; 1368 const char* superclassDescriptor; 1369 char* accessStr = NULL; 1370 int i; 1371 1372 pClassDef = dexGetClassDef(pDexFile, idx); 1373 1374 if (gOptions.exportsOnly && (pClassDef->accessFlags & ACC_PUBLIC) == 0) { 1375 //printf("<!-- omitting non-public class %s -->\n", 1376 // classDescriptor); 1377 goto bail; 1378 } 1379 1380 pEncodedData = dexGetClassData(pDexFile, pClassDef); 1381 pClassData = dexReadAndVerifyClassData(&pEncodedData, NULL); 1382 1383 if (pClassData == NULL) { 1384 printf("Trouble reading class data (#%d)\n", idx); 1385 goto bail; 1386 } 1387 1388 classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx); 1389 1390 /* 1391 * For the XML output, show the package name. Ideally we'd gather 1392 * up the classes, sort them, and dump them alphabetically so the 1393 * package name wouldn't jump around, but that's not a great plan 1394 * for something that needs to run on the device. 1395 */ 1396 if (!(classDescriptor[0] == 'L' && 1397 classDescriptor[strlen(classDescriptor)-1] == ';')) 1398 { 1399 /* arrays and primitives should not be defined explicitly */ 1400 fprintf(stderr, "Malformed class name '%s'\n", classDescriptor); 1401 /* keep going? */ 1402 } else if (gOptions.outputFormat == OUTPUT_XML) { 1403 char* mangle; 1404 char* lastSlash; 1405 char* cp; 1406 1407 mangle = strdup(classDescriptor + 1); 1408 mangle[strlen(mangle)-1] = '\0'; 1409 1410 /* reduce to just the package name */ 1411 lastSlash = strrchr(mangle, '/'); 1412 if (lastSlash != NULL) { 1413 *lastSlash = '\0'; 1414 } else { 1415 *mangle = '\0'; 1416 } 1417 1418 for (cp = mangle; *cp != '\0'; cp++) { 1419 if (*cp == '/') 1420 *cp = '.'; 1421 } 1422 1423 if (*pLastPackage == NULL || strcmp(mangle, *pLastPackage) != 0) { 1424 /* start of a new package */ 1425 if (*pLastPackage != NULL) 1426 printf("</package>\n"); 1427 printf("<package name=\"%s\"\n>\n", mangle); 1428 free(*pLastPackage); 1429 *pLastPackage = mangle; 1430 } else { 1431 free(mangle); 1432 } 1433 } 1434 1435 accessStr = createAccessFlagStr(pClassDef->accessFlags, kAccessForClass); 1436 1437 if (pClassDef->superclassIdx == kDexNoIndex) { 1438 superclassDescriptor = NULL; 1439 } else { 1440 superclassDescriptor = 1441 dexStringByTypeIdx(pDexFile, pClassDef->superclassIdx); 1442 } 1443 1444 if (gOptions.outputFormat == OUTPUT_PLAIN) { 1445 printf("Class #%d -\n", idx); 1446 printf(" Class descriptor : '%s'\n", classDescriptor); 1447 printf(" Access flags : 0x%04x (%s)\n", 1448 pClassDef->accessFlags, accessStr); 1449 1450 if (superclassDescriptor != NULL) 1451 printf(" Superclass : '%s'\n", superclassDescriptor); 1452 1453 printf(" Interfaces -\n"); 1454 } else { 1455 char* tmp; 1456 1457 tmp = descriptorClassToDot(classDescriptor); 1458 printf("<class name=\"%s\"\n", tmp); 1459 free(tmp); 1460 1461 if (superclassDescriptor != NULL) { 1462 tmp = descriptorToDot(superclassDescriptor); 1463 printf(" extends=\"%s\"\n", tmp); 1464 free(tmp); 1465 } 1466 printf(" abstract=%s\n", 1467 quotedBool((pClassDef->accessFlags & ACC_ABSTRACT) != 0)); 1468 printf(" static=%s\n", 1469 quotedBool((pClassDef->accessFlags & ACC_STATIC) != 0)); 1470 printf(" final=%s\n", 1471 quotedBool((pClassDef->accessFlags & ACC_FINAL) != 0)); 1472 // "deprecated=" not knowable w/o parsing annotations 1473 printf(" visibility=%s\n", 1474 quotedVisibility(pClassDef->accessFlags)); 1475 printf(">\n"); 1476 } 1477 pInterfaces = dexGetInterfacesList(pDexFile, pClassDef); 1478 if (pInterfaces != NULL) { 1479 for (i = 0; i < (int) pInterfaces->size; i++) 1480 dumpInterface(pDexFile, dexGetTypeItem(pInterfaces, i), i); 1481 } 1482 1483 if (gOptions.outputFormat == OUTPUT_PLAIN) 1484 printf(" Static fields -\n"); 1485 for (i = 0; i < (int) pClassData->header.staticFieldsSize; i++) { 1486 dumpSField(pDexFile, &pClassData->staticFields[i], i); 1487 } 1488 1489 if (gOptions.outputFormat == OUTPUT_PLAIN) 1490 printf(" Instance fields -\n"); 1491 for (i = 0; i < (int) pClassData->header.instanceFieldsSize; i++) { 1492 dumpIField(pDexFile, &pClassData->instanceFields[i], i); 1493 } 1494 1495 if (gOptions.outputFormat == OUTPUT_PLAIN) 1496 printf(" Direct methods -\n"); 1497 for (i = 0; i < (int) pClassData->header.directMethodsSize; i++) { 1498 dumpMethod(pDexFile, &pClassData->directMethods[i], i); 1499 } 1500 1501 if (gOptions.outputFormat == OUTPUT_PLAIN) 1502 printf(" Virtual methods -\n"); 1503 for (i = 0; i < (int) pClassData->header.virtualMethodsSize; i++) { 1504 dumpMethod(pDexFile, &pClassData->virtualMethods[i], i); 1505 } 1506 1507 // TODO: Annotations. 1508 1509 if (pClassDef->sourceFileIdx != kDexNoIndex) 1510 fileName = dexStringById(pDexFile, pClassDef->sourceFileIdx); 1511 else 1512 fileName = "unknown"; 1513 1514 if (gOptions.outputFormat == OUTPUT_PLAIN) { 1515 printf(" source_file_idx : %d (%s)\n", 1516 pClassDef->sourceFileIdx, fileName); 1517 printf("\n"); 1518 } 1519 1520 if (gOptions.outputFormat == OUTPUT_XML) { 1521 printf("</class>\n"); 1522 } 1523 1524 bail: 1525 free(pClassData); 1526 free(accessStr); 1527 } 1528 1529 1530 /* 1531 * Advance "ptr" to ensure 32-bit alignment. 1532 */ 1533 static inline const u1* align32(const u1* ptr) 1534 { 1535 return (u1*) (((uintptr_t) ptr + 3) & ~0x03); 1536 } 1537 1538 1539 /* 1540 * Dump a map in the "differential" format. 1541 * 1542 * TODO: show a hex dump of the compressed data. (We can show the 1543 * uncompressed data if we move the compression code to libdex; otherwise 1544 * it's too complex to merit a fast & fragile implementation here.) 1545 */ 1546 void dumpDifferentialCompressedMap(const u1** pData) 1547 { 1548 const u1* data = *pData; 1549 const u1* dataStart = data -1; // format byte already removed 1550 u1 regWidth; 1551 u2 numEntries; 1552 1553 /* standard header */ 1554 regWidth = *data++; 1555 numEntries = *data++; 1556 numEntries |= (*data++) << 8; 1557 1558 /* compressed data begins with the compressed data length */ 1559 int compressedLen = readUnsignedLeb128(&data); 1560 int addrWidth = 1; 1561 if ((*data & 0x80) != 0) 1562 addrWidth++; 1563 1564 int origLen = 4 + (addrWidth + regWidth) * numEntries; 1565 int compLen = (data - dataStart) + compressedLen; 1566 1567 printf(" (differential compression %d -> %d [%d -> %d])\n", 1568 origLen, compLen, 1569 (addrWidth + regWidth) * numEntries, compressedLen); 1570 1571 /* skip past end of entry */ 1572 data += compressedLen; 1573 1574 *pData = data; 1575 } 1576 1577 /* 1578 * Dump register map contents of the current method. 1579 * 1580 * "*pData" should point to the start of the register map data. Advances 1581 * "*pData" to the start of the next map. 1582 */ 1583 void dumpMethodMap(DexFile* pDexFile, const DexMethod* pDexMethod, int idx, 1584 const u1** pData) 1585 { 1586 const u1* data = *pData; 1587 const DexMethodId* pMethodId; 1588 const char* name; 1589 int offset = data - (u1*) pDexFile->pOptHeader; 1590 1591 pMethodId = dexGetMethodId(pDexFile, pDexMethod->methodIdx); 1592 name = dexStringById(pDexFile, pMethodId->nameIdx); 1593 printf(" #%d: 0x%08x %s\n", idx, offset, name); 1594 1595 u1 format; 1596 int addrWidth; 1597 1598 format = *data++; 1599 if (format == 1) { /* kRegMapFormatNone */ 1600 /* no map */ 1601 printf(" (no map)\n"); 1602 addrWidth = 0; 1603 } else if (format == 2) { /* kRegMapFormatCompact8 */ 1604 addrWidth = 1; 1605 } else if (format == 3) { /* kRegMapFormatCompact16 */ 1606 addrWidth = 2; 1607 } else if (format == 4) { /* kRegMapFormatDifferential */ 1608 dumpDifferentialCompressedMap(&data); 1609 goto bail; 1610 } else { 1611 printf(" (unknown format %d!)\n", format); 1612 /* don't know how to skip data; failure will cascade to end of class */ 1613 goto bail; 1614 } 1615 1616 if (addrWidth > 0) { 1617 u1 regWidth; 1618 u2 numEntries; 1619 int idx, addr, byte; 1620 1621 regWidth = *data++; 1622 numEntries = *data++; 1623 numEntries |= (*data++) << 8; 1624 1625 for (idx = 0; idx < numEntries; idx++) { 1626 addr = *data++; 1627 if (addrWidth > 1) 1628 addr |= (*data++) << 8; 1629 1630 printf(" %4x:", addr); 1631 for (byte = 0; byte < regWidth; byte++) { 1632 printf(" %02x", *data++); 1633 } 1634 printf("\n"); 1635 } 1636 } 1637 1638 bail: 1639 //if (addrWidth >= 0) 1640 // *pData = align32(data); 1641 *pData = data; 1642 } 1643 1644 /* 1645 * Dump the contents of the register map area. 1646 * 1647 * These are only present in optimized DEX files, and the structure is 1648 * not really exposed to other parts of the VM itself. We're going to 1649 * dig through them here, but this is pretty fragile. DO NOT rely on 1650 * this or derive other code from it. 1651 */ 1652 void dumpRegisterMaps(DexFile* pDexFile) 1653 { 1654 const u1* pClassPool = (const u1*)pDexFile->pRegisterMapPool; 1655 const u4* classOffsets; 1656 const u1* ptr; 1657 u4 numClasses; 1658 int baseFileOffset = (u1*) pClassPool - (u1*) pDexFile->pOptHeader; 1659 int idx; 1660 1661 if (pClassPool == NULL) { 1662 printf("No register maps found\n"); 1663 return; 1664 } 1665 1666 ptr = pClassPool; 1667 numClasses = get4LE(ptr); 1668 ptr += sizeof(u4); 1669 classOffsets = (const u4*) ptr; 1670 1671 printf("RMAP begins at offset 0x%07x\n", baseFileOffset); 1672 printf("Maps for %d classes\n", numClasses); 1673 for (idx = 0; idx < (int) numClasses; idx++) { 1674 const DexClassDef* pClassDef; 1675 const char* classDescriptor; 1676 1677 pClassDef = dexGetClassDef(pDexFile, idx); 1678 classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx); 1679 1680 printf("%4d: +%d (0x%08x) %s\n", idx, classOffsets[idx], 1681 baseFileOffset + classOffsets[idx], classDescriptor); 1682 1683 if (classOffsets[idx] == 0) 1684 continue; 1685 1686 /* 1687 * What follows is a series of RegisterMap entries, one for every 1688 * direct method, then one for every virtual method. 1689 */ 1690 DexClassData* pClassData; 1691 const u1* pEncodedData; 1692 const u1* data = (u1*) pClassPool + classOffsets[idx]; 1693 u2 methodCount; 1694 int i; 1695 1696 pEncodedData = dexGetClassData(pDexFile, pClassDef); 1697 pClassData = dexReadAndVerifyClassData(&pEncodedData, NULL); 1698 if (pClassData == NULL) { 1699 fprintf(stderr, "Trouble reading class data\n"); 1700 continue; 1701 } 1702 1703 methodCount = *data++; 1704 methodCount |= (*data++) << 8; 1705 data += 2; /* two pad bytes follow methodCount */ 1706 if (methodCount != pClassData->header.directMethodsSize 1707 + pClassData->header.virtualMethodsSize) 1708 { 1709 printf("NOTE: method count discrepancy (%d != %d + %d)\n", 1710 methodCount, pClassData->header.directMethodsSize, 1711 pClassData->header.virtualMethodsSize); 1712 /* this is bad, but keep going anyway */ 1713 } 1714 1715 printf(" direct methods: %d\n", 1716 pClassData->header.directMethodsSize); 1717 for (i = 0; i < (int) pClassData->header.directMethodsSize; i++) { 1718 dumpMethodMap(pDexFile, &pClassData->directMethods[i], i, &data); 1719 } 1720 1721 printf(" virtual methods: %d\n", 1722 pClassData->header.virtualMethodsSize); 1723 for (i = 0; i < (int) pClassData->header.virtualMethodsSize; i++) { 1724 dumpMethodMap(pDexFile, &pClassData->virtualMethods[i], i, &data); 1725 } 1726 1727 free(pClassData); 1728 } 1729 } 1730 1731 /* 1732 * Dump the requested sections of the file. 1733 */ 1734 void processDexFile(const char* fileName, DexFile* pDexFile) 1735 { 1736 char* package = NULL; 1737 int i; 1738 1739 if (gOptions.verbose) { 1740 printf("Opened '%s', DEX version '%.3s'\n", fileName, 1741 pDexFile->pHeader->magic +4); 1742 } 1743 1744 if (gOptions.dumpRegisterMaps) { 1745 dumpRegisterMaps(pDexFile); 1746 return; 1747 } 1748 1749 if (gOptions.showFileHeaders) { 1750 dumpFileHeader(pDexFile); 1751 dumpOptDirectory(pDexFile); 1752 } 1753 1754 if (gOptions.outputFormat == OUTPUT_XML) 1755 printf("<api>\n"); 1756 1757 for (i = 0; i < (int) pDexFile->pHeader->classDefsSize; i++) { 1758 if (gOptions.showSectionHeaders) 1759 dumpClassDef(pDexFile, i); 1760 1761 dumpClass(pDexFile, i, &package); 1762 } 1763 1764 /* free the last one allocated */ 1765 if (package != NULL) { 1766 printf("</package>\n"); 1767 free(package); 1768 } 1769 1770 if (gOptions.outputFormat == OUTPUT_XML) 1771 printf("</api>\n"); 1772 } 1773 1774 1775 /* 1776 * Process one file. 1777 */ 1778 int process(const char* fileName) 1779 { 1780 DexFile* pDexFile = NULL; 1781 MemMapping map; 1782 bool mapped = false; 1783 int result = -1; 1784 1785 if (gOptions.verbose) 1786 printf("Processing '%s'...\n", fileName); 1787 1788 if (dexOpenAndMap(fileName, gOptions.tempFileName, &map, false) != 0) { 1789 return result; 1790 } 1791 mapped = true; 1792 1793 int flags = kDexParseVerifyChecksum; 1794 if (gOptions.ignoreBadChecksum) 1795 flags |= kDexParseContinueOnError; 1796 1797 pDexFile = dexFileParse((u1*)map.addr, map.length, flags); 1798 if (pDexFile == NULL) { 1799 fprintf(stderr, "ERROR: DEX parse failed\n"); 1800 goto bail; 1801 } 1802 1803 if (gOptions.checksumOnly) { 1804 printf("Checksum verified\n"); 1805 } else { 1806 processDexFile(fileName, pDexFile); 1807 } 1808 1809 result = 0; 1810 1811 bail: 1812 if (mapped) 1813 sysReleaseShmem(&map); 1814 if (pDexFile != NULL) 1815 dexFileFree(pDexFile); 1816 return result; 1817 } 1818 1819 1820 /* 1821 * Show usage. 1822 */ 1823 void usage(void) 1824 { 1825 fprintf(stderr, "Copyright (C) 2007 The Android Open Source Project\n\n"); 1826 fprintf(stderr, 1827 "%s: [-c] [-d] [-f] [-h] [-i] [-l layout] [-m] [-t tempfile] dexfile...\n", 1828 gProgName); 1829 fprintf(stderr, "\n"); 1830 fprintf(stderr, " -c : verify checksum and exit\n"); 1831 fprintf(stderr, " -d : disassemble code sections\n"); 1832 fprintf(stderr, " -f : display summary information from file header\n"); 1833 fprintf(stderr, " -h : display file header details\n"); 1834 fprintf(stderr, " -i : ignore checksum failures\n"); 1835 fprintf(stderr, " -l : output layout, either 'plain' or 'xml'\n"); 1836 fprintf(stderr, " -m : dump register maps (and nothing else)\n"); 1837 fprintf(stderr, " -t : temp file name (defaults to /sdcard/dex-temp-*)\n"); 1838 } 1839 1840 /* 1841 * Parse args. 1842 * 1843 * I'm not using getopt_long() because we may not have it in libc. 1844 */ 1845 int main(int argc, char* const argv[]) 1846 { 1847 bool wantUsage = false; 1848 int ic; 1849 1850 memset(&gOptions, 0, sizeof(gOptions)); 1851 gOptions.verbose = true; 1852 1853 while (1) { 1854 ic = getopt(argc, argv, "cdfhil:mt:"); 1855 if (ic < 0) 1856 break; 1857 1858 switch (ic) { 1859 case 'c': // verify the checksum then exit 1860 gOptions.checksumOnly = true; 1861 break; 1862 case 'd': // disassemble Dalvik instructions 1863 gOptions.disassemble = true; 1864 break; 1865 case 'f': // dump outer file header 1866 gOptions.showFileHeaders = true; 1867 break; 1868 case 'h': // dump section headers, i.e. all meta-data 1869 gOptions.showSectionHeaders = true; 1870 break; 1871 case 'i': // continue even if checksum is bad 1872 gOptions.ignoreBadChecksum = true; 1873 break; 1874 case 'l': // layout 1875 if (strcmp(optarg, "plain") == 0) { 1876 gOptions.outputFormat = OUTPUT_PLAIN; 1877 } else if (strcmp(optarg, "xml") == 0) { 1878 gOptions.outputFormat = OUTPUT_XML; 1879 gOptions.verbose = false; 1880 gOptions.exportsOnly = true; 1881 } else { 1882 wantUsage = true; 1883 } 1884 break; 1885 case 'm': // dump register maps only 1886 gOptions.dumpRegisterMaps = true; 1887 break; 1888 case 't': // temp file, used when opening compressed Jar 1889 gOptions.tempFileName = optarg; 1890 break; 1891 default: 1892 wantUsage = true; 1893 break; 1894 } 1895 } 1896 1897 if (optind == argc) { 1898 fprintf(stderr, "%s: no file specified\n", gProgName); 1899 wantUsage = true; 1900 } 1901 1902 if (gOptions.checksumOnly && gOptions.ignoreBadChecksum) { 1903 fprintf(stderr, "Can't specify both -c and -i\n"); 1904 wantUsage = true; 1905 } 1906 1907 if (wantUsage) { 1908 usage(); 1909 return 2; 1910 } 1911 1912 int result = 0; 1913 while (optind < argc) { 1914 result |= process(argv[optind++]); 1915 } 1916 1917 return (result != 0); 1918 } 1919