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