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