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