1 /* 2 * Copyright (C) 2015 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 * Implementation file of the dexdump utility. 17 * 18 * This is a re-implementation of the original dexdump utility that was 19 * based on Dalvik functions in libdex into a new dexdump that is now 20 * based on Art functions in libart instead. The output is identical to 21 * the original for correct DEX files. Error messages may differ, however. 22 * Also, ODEX files are no longer supported. 23 * 24 * The dexdump tool is intended to mimic objdump. When possible, use 25 * similar command-line arguments. 26 * 27 * Differences between XML output and the "current.xml" file: 28 * - classes in same package are not all grouped together; nothing is sorted 29 * - no "deprecated" on fields and methods 30 * - no parameter names 31 * - no generic signatures on parameters, e.g. type="java.lang.Class<?>" 32 * - class shows declared fields and methods; does not show inherited fields 33 */ 34 35 #include "dexdump.h" 36 37 #include <inttypes.h> 38 #include <stdio.h> 39 40 #include <iostream> 41 #include <memory> 42 #include <sstream> 43 #include <vector> 44 45 #include "dex_file-inl.h" 46 #include "dex_instruction-inl.h" 47 #include "utils.h" 48 49 namespace art { 50 51 /* 52 * Options parsed in main driver. 53 */ 54 struct Options gOptions; 55 56 /* 57 * Output file. Defaults to stdout. 58 */ 59 FILE* gOutFile = stdout; 60 61 /* 62 * Data types that match the definitions in the VM specification. 63 */ 64 typedef uint8_t u1; 65 typedef uint16_t u2; 66 typedef uint32_t u4; 67 typedef uint64_t u8; 68 typedef int32_t s4; 69 typedef int64_t s8; 70 71 /* 72 * Basic information about a field or a method. 73 */ 74 struct FieldMethodInfo { 75 const char* classDescriptor; 76 const char* name; 77 const char* signature; 78 }; 79 80 /* 81 * Flags for use with createAccessFlagStr(). 82 */ 83 enum AccessFor { 84 kAccessForClass = 0, kAccessForMethod = 1, kAccessForField = 2, kAccessForMAX 85 }; 86 const int kNumFlags = 18; 87 88 /* 89 * Gets 2 little-endian bytes. 90 */ 91 static inline u2 get2LE(unsigned char const* pSrc) { 92 return pSrc[0] | (pSrc[1] << 8); 93 } 94 95 /* 96 * Converts a single-character primitive type into human-readable form. 97 */ 98 static const char* primitiveTypeLabel(char typeChar) { 99 switch (typeChar) { 100 case 'B': return "byte"; 101 case 'C': return "char"; 102 case 'D': return "double"; 103 case 'F': return "float"; 104 case 'I': return "int"; 105 case 'J': return "long"; 106 case 'S': return "short"; 107 case 'V': return "void"; 108 case 'Z': return "boolean"; 109 default: return "UNKNOWN"; 110 } // switch 111 } 112 113 /* 114 * Converts a type descriptor to human-readable "dotted" form. For 115 * example, "Ljava/lang/String;" becomes "java.lang.String", and 116 * "[I" becomes "int[]". Also converts '$' to '.', which means this 117 * form can't be converted back to a descriptor. 118 */ 119 static char* descriptorToDot(const char* str) { 120 int targetLen = strlen(str); 121 int offset = 0; 122 123 // Strip leading [s; will be added to end. 124 while (targetLen > 1 && str[offset] == '[') { 125 offset++; 126 targetLen--; 127 } // while 128 129 const int arrayDepth = offset; 130 131 if (targetLen == 1) { 132 // Primitive type. 133 str = primitiveTypeLabel(str[offset]); 134 offset = 0; 135 targetLen = strlen(str); 136 } else { 137 // Account for leading 'L' and trailing ';'. 138 if (targetLen >= 2 && str[offset] == 'L' && 139 str[offset + targetLen - 1] == ';') { 140 targetLen -= 2; 141 offset++; 142 } 143 } 144 145 // Copy class name over. 146 char* newStr = reinterpret_cast<char*>( 147 malloc(targetLen + arrayDepth * 2 + 1)); 148 int i = 0; 149 for (; i < targetLen; i++) { 150 const char ch = str[offset + i]; 151 newStr[i] = (ch == '/' || ch == '$') ? '.' : ch; 152 } // for 153 154 // Add the appropriate number of brackets for arrays. 155 for (int j = 0; j < arrayDepth; j++) { 156 newStr[i++] = '['; 157 newStr[i++] = ']'; 158 } // for 159 160 newStr[i] = '\0'; 161 return newStr; 162 } 163 164 /* 165 * Converts the class name portion of a type descriptor to human-readable 166 * "dotted" form. 167 * 168 * Returns a newly-allocated string. 169 */ 170 static char* descriptorClassToDot(const char* str) { 171 // Reduce to just the class name, trimming trailing ';'. 172 const char* lastSlash = strrchr(str, '/'); 173 if (lastSlash == nullptr) { 174 lastSlash = str + 1; // start past 'L' 175 } else { 176 lastSlash++; // start past '/' 177 } 178 179 char* newStr = strdup(lastSlash); 180 newStr[strlen(lastSlash) - 1] = '\0'; 181 for (char* cp = newStr; *cp != '\0'; cp++) { 182 if (*cp == '$') { 183 *cp = '.'; 184 } 185 } // for 186 return newStr; 187 } 188 189 /* 190 * Returns a quoted string representing the boolean value. 191 */ 192 static const char* quotedBool(bool val) { 193 return val ? "\"true\"" : "\"false\""; 194 } 195 196 /* 197 * Returns a quoted string representing the access flags. 198 */ 199 static const char* quotedVisibility(u4 accessFlags) { 200 if (accessFlags & kAccPublic) { 201 return "\"public\""; 202 } else if (accessFlags & kAccProtected) { 203 return "\"protected\""; 204 } else if (accessFlags & kAccPrivate) { 205 return "\"private\""; 206 } else { 207 return "\"package\""; 208 } 209 } 210 211 /* 212 * Counts the number of '1' bits in a word. 213 */ 214 static int countOnes(u4 val) { 215 val = val - ((val >> 1) & 0x55555555); 216 val = (val & 0x33333333) + ((val >> 2) & 0x33333333); 217 return (((val + (val >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24; 218 } 219 220 /* 221 * Creates a new string with human-readable access flags. 222 * 223 * In the base language the access_flags fields are type u2; in Dalvik 224 * they're u4. 225 */ 226 static char* createAccessFlagStr(u4 flags, AccessFor forWhat) { 227 static const char* kAccessStrings[kAccessForMAX][kNumFlags] = { 228 { 229 "PUBLIC", /* 0x00001 */ 230 "PRIVATE", /* 0x00002 */ 231 "PROTECTED", /* 0x00004 */ 232 "STATIC", /* 0x00008 */ 233 "FINAL", /* 0x00010 */ 234 "?", /* 0x00020 */ 235 "?", /* 0x00040 */ 236 "?", /* 0x00080 */ 237 "?", /* 0x00100 */ 238 "INTERFACE", /* 0x00200 */ 239 "ABSTRACT", /* 0x00400 */ 240 "?", /* 0x00800 */ 241 "SYNTHETIC", /* 0x01000 */ 242 "ANNOTATION", /* 0x02000 */ 243 "ENUM", /* 0x04000 */ 244 "?", /* 0x08000 */ 245 "VERIFIED", /* 0x10000 */ 246 "OPTIMIZED", /* 0x20000 */ 247 }, { 248 "PUBLIC", /* 0x00001 */ 249 "PRIVATE", /* 0x00002 */ 250 "PROTECTED", /* 0x00004 */ 251 "STATIC", /* 0x00008 */ 252 "FINAL", /* 0x00010 */ 253 "SYNCHRONIZED", /* 0x00020 */ 254 "BRIDGE", /* 0x00040 */ 255 "VARARGS", /* 0x00080 */ 256 "NATIVE", /* 0x00100 */ 257 "?", /* 0x00200 */ 258 "ABSTRACT", /* 0x00400 */ 259 "STRICT", /* 0x00800 */ 260 "SYNTHETIC", /* 0x01000 */ 261 "?", /* 0x02000 */ 262 "?", /* 0x04000 */ 263 "MIRANDA", /* 0x08000 */ 264 "CONSTRUCTOR", /* 0x10000 */ 265 "DECLARED_SYNCHRONIZED", /* 0x20000 */ 266 }, { 267 "PUBLIC", /* 0x00001 */ 268 "PRIVATE", /* 0x00002 */ 269 "PROTECTED", /* 0x00004 */ 270 "STATIC", /* 0x00008 */ 271 "FINAL", /* 0x00010 */ 272 "?", /* 0x00020 */ 273 "VOLATILE", /* 0x00040 */ 274 "TRANSIENT", /* 0x00080 */ 275 "?", /* 0x00100 */ 276 "?", /* 0x00200 */ 277 "?", /* 0x00400 */ 278 "?", /* 0x00800 */ 279 "SYNTHETIC", /* 0x01000 */ 280 "?", /* 0x02000 */ 281 "ENUM", /* 0x04000 */ 282 "?", /* 0x08000 */ 283 "?", /* 0x10000 */ 284 "?", /* 0x20000 */ 285 }, 286 }; 287 288 // Allocate enough storage to hold the expected number of strings, 289 // plus a space between each. We over-allocate, using the longest 290 // string above as the base metric. 291 const int kLongest = 21; // The strlen of longest string above. 292 const int count = countOnes(flags); 293 char* str; 294 char* cp; 295 cp = str = reinterpret_cast<char*>(malloc(count * (kLongest + 1) + 1)); 296 297 for (int i = 0; i < kNumFlags; i++) { 298 if (flags & 0x01) { 299 const char* accessStr = kAccessStrings[forWhat][i]; 300 const int len = strlen(accessStr); 301 if (cp != str) { 302 *cp++ = ' '; 303 } 304 memcpy(cp, accessStr, len); 305 cp += len; 306 } 307 flags >>= 1; 308 } // for 309 310 *cp = '\0'; 311 return str; 312 } 313 314 /* 315 * Copies character data from "data" to "out", converting non-ASCII values 316 * to fprintf format chars or an ASCII filler ('.' or '?'). 317 * 318 * The output buffer must be able to hold (2*len)+1 bytes. The result is 319 * NULL-terminated. 320 */ 321 static void asciify(char* out, const unsigned char* data, size_t len) { 322 while (len--) { 323 if (*data < 0x20) { 324 // Could do more here, but we don't need them yet. 325 switch (*data) { 326 case '\0': 327 *out++ = '\\'; 328 *out++ = '0'; 329 break; 330 case '\n': 331 *out++ = '\\'; 332 *out++ = 'n'; 333 break; 334 default: 335 *out++ = '.'; 336 break; 337 } // switch 338 } else if (*data >= 0x80) { 339 *out++ = '?'; 340 } else { 341 *out++ = *data; 342 } 343 data++; 344 } // while 345 *out = '\0'; 346 } 347 348 /* 349 * Dumps the file header. 350 * 351 * Note that some of the : are misaligned on purpose to preserve 352 * the exact output of the original Dalvik dexdump. 353 */ 354 static void dumpFileHeader(const DexFile* pDexFile) { 355 const DexFile::Header& pHeader = pDexFile->GetHeader(); 356 char sanitized[sizeof(pHeader.magic_) * 2 + 1]; 357 fprintf(gOutFile, "DEX file header:\n"); 358 asciify(sanitized, pHeader.magic_, sizeof(pHeader.magic_)); 359 fprintf(gOutFile, "magic : '%s'\n", sanitized); 360 fprintf(gOutFile, "checksum : %08x\n", pHeader.checksum_); 361 fprintf(gOutFile, "signature : %02x%02x...%02x%02x\n", 362 pHeader.signature_[0], pHeader.signature_[1], 363 pHeader.signature_[DexFile::kSha1DigestSize - 2], 364 pHeader.signature_[DexFile::kSha1DigestSize - 1]); 365 fprintf(gOutFile, "file_size : %d\n", pHeader.file_size_); 366 fprintf(gOutFile, "header_size : %d\n", pHeader.header_size_); 367 fprintf(gOutFile, "link_size : %d\n", pHeader.link_size_); 368 fprintf(gOutFile, "link_off : %d (0x%06x)\n", 369 pHeader.link_off_, pHeader.link_off_); 370 fprintf(gOutFile, "string_ids_size : %d\n", pHeader.string_ids_size_); 371 fprintf(gOutFile, "string_ids_off : %d (0x%06x)\n", 372 pHeader.string_ids_off_, pHeader.string_ids_off_); 373 fprintf(gOutFile, "type_ids_size : %d\n", pHeader.type_ids_size_); 374 fprintf(gOutFile, "type_ids_off : %d (0x%06x)\n", 375 pHeader.type_ids_off_, pHeader.type_ids_off_); 376 fprintf(gOutFile, "proto_ids_size : %d\n", pHeader.proto_ids_size_); 377 fprintf(gOutFile, "proto_ids_off : %d (0x%06x)\n", 378 pHeader.proto_ids_off_, pHeader.proto_ids_off_); 379 fprintf(gOutFile, "field_ids_size : %d\n", pHeader.field_ids_size_); 380 fprintf(gOutFile, "field_ids_off : %d (0x%06x)\n", 381 pHeader.field_ids_off_, pHeader.field_ids_off_); 382 fprintf(gOutFile, "method_ids_size : %d\n", pHeader.method_ids_size_); 383 fprintf(gOutFile, "method_ids_off : %d (0x%06x)\n", 384 pHeader.method_ids_off_, pHeader.method_ids_off_); 385 fprintf(gOutFile, "class_defs_size : %d\n", pHeader.class_defs_size_); 386 fprintf(gOutFile, "class_defs_off : %d (0x%06x)\n", 387 pHeader.class_defs_off_, pHeader.class_defs_off_); 388 fprintf(gOutFile, "data_size : %d\n", pHeader.data_size_); 389 fprintf(gOutFile, "data_off : %d (0x%06x)\n\n", 390 pHeader.data_off_, pHeader.data_off_); 391 } 392 393 /* 394 * Dumps a class_def_item. 395 */ 396 static void dumpClassDef(const DexFile* pDexFile, int idx) { 397 // General class information. 398 const DexFile::ClassDef& pClassDef = pDexFile->GetClassDef(idx); 399 fprintf(gOutFile, "Class #%d header:\n", idx); 400 fprintf(gOutFile, "class_idx : %d\n", pClassDef.class_idx_); 401 fprintf(gOutFile, "access_flags : %d (0x%04x)\n", 402 pClassDef.access_flags_, pClassDef.access_flags_); 403 fprintf(gOutFile, "superclass_idx : %d\n", pClassDef.superclass_idx_); 404 fprintf(gOutFile, "interfaces_off : %d (0x%06x)\n", 405 pClassDef.interfaces_off_, pClassDef.interfaces_off_); 406 fprintf(gOutFile, "source_file_idx : %d\n", pClassDef.source_file_idx_); 407 fprintf(gOutFile, "annotations_off : %d (0x%06x)\n", 408 pClassDef.annotations_off_, pClassDef.annotations_off_); 409 fprintf(gOutFile, "class_data_off : %d (0x%06x)\n", 410 pClassDef.class_data_off_, pClassDef.class_data_off_); 411 412 // Fields and methods. 413 const u1* pEncodedData = pDexFile->GetClassData(pClassDef); 414 if (pEncodedData != nullptr) { 415 ClassDataItemIterator pClassData(*pDexFile, pEncodedData); 416 fprintf(gOutFile, "static_fields_size : %d\n", pClassData.NumStaticFields()); 417 fprintf(gOutFile, "instance_fields_size: %d\n", pClassData.NumInstanceFields()); 418 fprintf(gOutFile, "direct_methods_size : %d\n", pClassData.NumDirectMethods()); 419 fprintf(gOutFile, "virtual_methods_size: %d\n", pClassData.NumVirtualMethods()); 420 } else { 421 fprintf(gOutFile, "static_fields_size : 0\n"); 422 fprintf(gOutFile, "instance_fields_size: 0\n"); 423 fprintf(gOutFile, "direct_methods_size : 0\n"); 424 fprintf(gOutFile, "virtual_methods_size: 0\n"); 425 } 426 fprintf(gOutFile, "\n"); 427 } 428 429 /* 430 * Dumps an interface that a class declares to implement. 431 */ 432 static void dumpInterface(const DexFile* pDexFile, const DexFile::TypeItem& pTypeItem, int i) { 433 const char* interfaceName = pDexFile->StringByTypeIdx(pTypeItem.type_idx_); 434 if (gOptions.outputFormat == OUTPUT_PLAIN) { 435 fprintf(gOutFile, " #%d : '%s'\n", i, interfaceName); 436 } else { 437 char* dotted = descriptorToDot(interfaceName); 438 fprintf(gOutFile, "<implements name=\"%s\">\n</implements>\n", dotted); 439 free(dotted); 440 } 441 } 442 443 /* 444 * Dumps the catches table associated with the code. 445 */ 446 static void dumpCatches(const DexFile* pDexFile, const DexFile::CodeItem* pCode) { 447 const u4 triesSize = pCode->tries_size_; 448 449 // No catch table. 450 if (triesSize == 0) { 451 fprintf(gOutFile, " catches : (none)\n"); 452 return; 453 } 454 455 // Dump all table entries. 456 fprintf(gOutFile, " catches : %d\n", triesSize); 457 for (u4 i = 0; i < triesSize; i++) { 458 const DexFile::TryItem* pTry = pDexFile->GetTryItems(*pCode, i); 459 const u4 start = pTry->start_addr_; 460 const u4 end = start + pTry->insn_count_; 461 fprintf(gOutFile, " 0x%04x - 0x%04x\n", start, end); 462 for (CatchHandlerIterator it(*pCode, *pTry); it.HasNext(); it.Next()) { 463 const u2 tidx = it.GetHandlerTypeIndex(); 464 const char* descriptor = 465 (tidx == DexFile::kDexNoIndex16) ? "<any>" : pDexFile->StringByTypeIdx(tidx); 466 fprintf(gOutFile, " %s -> 0x%04x\n", descriptor, it.GetHandlerAddress()); 467 } // for 468 } // for 469 } 470 471 /* 472 * Callback for dumping each positions table entry. 473 */ 474 static bool dumpPositionsCb(void* /*context*/, const DexFile::PositionInfo& entry) { 475 fprintf(gOutFile, " 0x%04x line=%d\n", entry.address_, entry.line_); 476 return false; 477 } 478 479 /* 480 * Callback for dumping locals table entry. 481 */ 482 static void dumpLocalsCb(void* /*context*/, const DexFile::LocalInfo& entry) { 483 const char* signature = entry.signature_ != nullptr ? entry.signature_ : ""; 484 fprintf(gOutFile, " 0x%04x - 0x%04x reg=%d %s %s %s\n", 485 entry.start_address_, entry.end_address_, entry.reg_, 486 entry.name_, entry.descriptor_, signature); 487 } 488 489 /* 490 * Helper for dumpInstruction(), which builds the string 491 * representation for the index in the given instruction. This will 492 * first try to use the given buffer, but if the result won't fit, 493 * then this will allocate a new buffer to hold the result. A pointer 494 * to the buffer which holds the full result is always returned, and 495 * this can be compared with the one passed in, to see if the result 496 * needs to be free()d. 497 */ 498 static char* indexString(const DexFile* pDexFile, 499 const Instruction* pDecInsn, char* buf, size_t bufSize) { 500 // Determine index and width of the string. 501 u4 index = 0; 502 u4 width = 4; 503 switch (Instruction::FormatOf(pDecInsn->Opcode())) { 504 // SOME NOT SUPPORTED: 505 // case Instruction::k20bc: 506 case Instruction::k21c: 507 case Instruction::k35c: 508 // case Instruction::k35ms: 509 case Instruction::k3rc: 510 // case Instruction::k3rms: 511 // case Instruction::k35mi: 512 // case Instruction::k3rmi: 513 index = pDecInsn->VRegB(); 514 width = 4; 515 break; 516 case Instruction::k31c: 517 index = pDecInsn->VRegB(); 518 width = 8; 519 break; 520 case Instruction::k22c: 521 // case Instruction::k22cs: 522 index = pDecInsn->VRegC(); 523 width = 4; 524 break; 525 default: 526 break; 527 } // switch 528 529 // Determine index type. 530 size_t outSize = 0; 531 switch (Instruction::IndexTypeOf(pDecInsn->Opcode())) { 532 case Instruction::kIndexUnknown: 533 // This function should never get called for this type, but do 534 // something sensible here, just to help with debugging. 535 outSize = snprintf(buf, bufSize, "<unknown-index>"); 536 break; 537 case Instruction::kIndexNone: 538 // This function should never get called for this type, but do 539 // something sensible here, just to help with debugging. 540 outSize = snprintf(buf, bufSize, "<no-index>"); 541 break; 542 case Instruction::kIndexTypeRef: 543 if (index < pDexFile->GetHeader().type_ids_size_) { 544 const char* tp = pDexFile->StringByTypeIdx(index); 545 outSize = snprintf(buf, bufSize, "%s // type@%0*x", tp, width, index); 546 } else { 547 outSize = snprintf(buf, bufSize, "<type?> // type@%0*x", width, index); 548 } 549 break; 550 case Instruction::kIndexStringRef: 551 if (index < pDexFile->GetHeader().string_ids_size_) { 552 const char* st = pDexFile->StringDataByIdx(index); 553 outSize = snprintf(buf, bufSize, "\"%s\" // string@%0*x", st, width, index); 554 } else { 555 outSize = snprintf(buf, bufSize, "<string?> // string@%0*x", width, index); 556 } 557 break; 558 case Instruction::kIndexMethodRef: 559 if (index < pDexFile->GetHeader().method_ids_size_) { 560 const DexFile::MethodId& pMethodId = pDexFile->GetMethodId(index); 561 const char* name = pDexFile->StringDataByIdx(pMethodId.name_idx_); 562 const Signature signature = pDexFile->GetMethodSignature(pMethodId); 563 const char* backDescriptor = pDexFile->StringByTypeIdx(pMethodId.class_idx_); 564 outSize = snprintf(buf, bufSize, "%s.%s:%s // method@%0*x", 565 backDescriptor, name, signature.ToString().c_str(), width, index); 566 } else { 567 outSize = snprintf(buf, bufSize, "<method?> // method@%0*x", width, index); 568 } 569 break; 570 case Instruction::kIndexFieldRef: 571 if (index < pDexFile->GetHeader().field_ids_size_) { 572 const DexFile::FieldId& pFieldId = pDexFile->GetFieldId(index); 573 const char* name = pDexFile->StringDataByIdx(pFieldId.name_idx_); 574 const char* typeDescriptor = pDexFile->StringByTypeIdx(pFieldId.type_idx_); 575 const char* backDescriptor = pDexFile->StringByTypeIdx(pFieldId.class_idx_); 576 outSize = snprintf(buf, bufSize, "%s.%s:%s // field@%0*x", 577 backDescriptor, name, typeDescriptor, width, index); 578 } else { 579 outSize = snprintf(buf, bufSize, "<field?> // field@%0*x", width, index); 580 } 581 break; 582 case Instruction::kIndexVtableOffset: 583 outSize = snprintf(buf, bufSize, "[%0*x] // vtable #%0*x", 584 width, index, width, index); 585 break; 586 case Instruction::kIndexFieldOffset: 587 outSize = snprintf(buf, bufSize, "[obj+%0*x]", width, index); 588 break; 589 // SOME NOT SUPPORTED: 590 // case Instruction::kIndexVaries: 591 // case Instruction::kIndexInlineMethod: 592 default: 593 outSize = snprintf(buf, bufSize, "<?>"); 594 break; 595 } // switch 596 597 // Determine success of string construction. 598 if (outSize >= bufSize) { 599 // The buffer wasn't big enough; allocate and retry. Note: 600 // snprintf() doesn't count the '\0' as part of its returned 601 // size, so we add explicit space for it here. 602 outSize++; 603 buf = reinterpret_cast<char*>(malloc(outSize)); 604 if (buf == nullptr) { 605 return nullptr; 606 } 607 return indexString(pDexFile, pDecInsn, buf, outSize); 608 } 609 return buf; 610 } 611 612 /* 613 * Dumps a single instruction. 614 */ 615 static void dumpInstruction(const DexFile* pDexFile, 616 const DexFile::CodeItem* pCode, 617 u4 codeOffset, u4 insnIdx, u4 insnWidth, 618 const Instruction* pDecInsn) { 619 // Address of instruction (expressed as byte offset). 620 fprintf(gOutFile, "%06x:", codeOffset + 0x10 + insnIdx * 2); 621 622 // Dump (part of) raw bytes. 623 const u2* insns = pCode->insns_; 624 for (u4 i = 0; i < 8; i++) { 625 if (i < insnWidth) { 626 if (i == 7) { 627 fprintf(gOutFile, " ... "); 628 } else { 629 // Print 16-bit value in little-endian order. 630 const u1* bytePtr = (const u1*) &insns[insnIdx + i]; 631 fprintf(gOutFile, " %02x%02x", bytePtr[0], bytePtr[1]); 632 } 633 } else { 634 fputs(" ", gOutFile); 635 } 636 } // for 637 638 // Dump pseudo-instruction or opcode. 639 if (pDecInsn->Opcode() == Instruction::NOP) { 640 const u2 instr = get2LE((const u1*) &insns[insnIdx]); 641 if (instr == Instruction::kPackedSwitchSignature) { 642 fprintf(gOutFile, "|%04x: packed-switch-data (%d units)", insnIdx, insnWidth); 643 } else if (instr == Instruction::kSparseSwitchSignature) { 644 fprintf(gOutFile, "|%04x: sparse-switch-data (%d units)", insnIdx, insnWidth); 645 } else if (instr == Instruction::kArrayDataSignature) { 646 fprintf(gOutFile, "|%04x: array-data (%d units)", insnIdx, insnWidth); 647 } else { 648 fprintf(gOutFile, "|%04x: nop // spacer", insnIdx); 649 } 650 } else { 651 fprintf(gOutFile, "|%04x: %s", insnIdx, pDecInsn->Name()); 652 } 653 654 // Set up additional argument. 655 char indexBufChars[200]; 656 char *indexBuf = indexBufChars; 657 if (Instruction::IndexTypeOf(pDecInsn->Opcode()) != Instruction::kIndexNone) { 658 indexBuf = indexString(pDexFile, pDecInsn, 659 indexBufChars, sizeof(indexBufChars)); 660 } 661 662 // Dump the instruction. 663 // 664 // NOTE: pDecInsn->DumpString(pDexFile) differs too much from original. 665 // 666 switch (Instruction::FormatOf(pDecInsn->Opcode())) { 667 case Instruction::k10x: // op 668 break; 669 case Instruction::k12x: // op vA, vB 670 fprintf(gOutFile, " v%d, v%d", pDecInsn->VRegA(), pDecInsn->VRegB()); 671 break; 672 case Instruction::k11n: // op vA, #+B 673 fprintf(gOutFile, " v%d, #int %d // #%x", 674 pDecInsn->VRegA(), (s4) pDecInsn->VRegB(), (u1)pDecInsn->VRegB()); 675 break; 676 case Instruction::k11x: // op vAA 677 fprintf(gOutFile, " v%d", pDecInsn->VRegA()); 678 break; 679 case Instruction::k10t: // op +AA 680 case Instruction::k20t: // op +AAAA 681 { 682 const s4 targ = (s4) pDecInsn->VRegA(); 683 fprintf(gOutFile, " %04x // %c%04x", 684 insnIdx + targ, 685 (targ < 0) ? '-' : '+', 686 (targ < 0) ? -targ : targ); 687 } 688 break; 689 case Instruction::k22x: // op vAA, vBBBB 690 fprintf(gOutFile, " v%d, v%d", pDecInsn->VRegA(), pDecInsn->VRegB()); 691 break; 692 case Instruction::k21t: // op vAA, +BBBB 693 { 694 const s4 targ = (s4) pDecInsn->VRegB(); 695 fprintf(gOutFile, " v%d, %04x // %c%04x", pDecInsn->VRegA(), 696 insnIdx + targ, 697 (targ < 0) ? '-' : '+', 698 (targ < 0) ? -targ : targ); 699 } 700 break; 701 case Instruction::k21s: // op vAA, #+BBBB 702 fprintf(gOutFile, " v%d, #int %d // #%x", 703 pDecInsn->VRegA(), (s4) pDecInsn->VRegB(), (u2)pDecInsn->VRegB()); 704 break; 705 case Instruction::k21h: // op vAA, #+BBBB0000[00000000] 706 // The printed format varies a bit based on the actual opcode. 707 if (pDecInsn->Opcode() == Instruction::CONST_HIGH16) { 708 const s4 value = pDecInsn->VRegB() << 16; 709 fprintf(gOutFile, " v%d, #int %d // #%x", 710 pDecInsn->VRegA(), value, (u2) pDecInsn->VRegB()); 711 } else { 712 const s8 value = ((s8) pDecInsn->VRegB()) << 48; 713 fprintf(gOutFile, " v%d, #long %" PRId64 " // #%x", 714 pDecInsn->VRegA(), value, (u2) pDecInsn->VRegB()); 715 } 716 break; 717 case Instruction::k21c: // op vAA, thing@BBBB 718 case Instruction::k31c: // op vAA, thing@BBBBBBBB 719 fprintf(gOutFile, " v%d, %s", pDecInsn->VRegA(), indexBuf); 720 break; 721 case Instruction::k23x: // op vAA, vBB, vCC 722 fprintf(gOutFile, " v%d, v%d, v%d", 723 pDecInsn->VRegA(), pDecInsn->VRegB(), pDecInsn->VRegC()); 724 break; 725 case Instruction::k22b: // op vAA, vBB, #+CC 726 fprintf(gOutFile, " v%d, v%d, #int %d // #%02x", 727 pDecInsn->VRegA(), pDecInsn->VRegB(), 728 (s4) pDecInsn->VRegC(), (u1) pDecInsn->VRegC()); 729 break; 730 case Instruction::k22t: // op vA, vB, +CCCC 731 { 732 const s4 targ = (s4) pDecInsn->VRegC(); 733 fprintf(gOutFile, " v%d, v%d, %04x // %c%04x", 734 pDecInsn->VRegA(), pDecInsn->VRegB(), 735 insnIdx + targ, 736 (targ < 0) ? '-' : '+', 737 (targ < 0) ? -targ : targ); 738 } 739 break; 740 case Instruction::k22s: // op vA, vB, #+CCCC 741 fprintf(gOutFile, " v%d, v%d, #int %d // #%04x", 742 pDecInsn->VRegA(), pDecInsn->VRegB(), 743 (s4) pDecInsn->VRegC(), (u2) pDecInsn->VRegC()); 744 break; 745 case Instruction::k22c: // op vA, vB, thing@CCCC 746 // NOT SUPPORTED: 747 // case Instruction::k22cs: // [opt] op vA, vB, field offset CCCC 748 fprintf(gOutFile, " v%d, v%d, %s", 749 pDecInsn->VRegA(), pDecInsn->VRegB(), indexBuf); 750 break; 751 case Instruction::k30t: 752 fprintf(gOutFile, " #%08x", pDecInsn->VRegA()); 753 break; 754 case Instruction::k31i: // op vAA, #+BBBBBBBB 755 { 756 // This is often, but not always, a float. 757 union { 758 float f; 759 u4 i; 760 } conv; 761 conv.i = pDecInsn->VRegB(); 762 fprintf(gOutFile, " v%d, #float %f // #%08x", 763 pDecInsn->VRegA(), conv.f, pDecInsn->VRegB()); 764 } 765 break; 766 case Instruction::k31t: // op vAA, offset +BBBBBBBB 767 fprintf(gOutFile, " v%d, %08x // +%08x", 768 pDecInsn->VRegA(), insnIdx + pDecInsn->VRegB(), pDecInsn->VRegB()); 769 break; 770 case Instruction::k32x: // op vAAAA, vBBBB 771 fprintf(gOutFile, " v%d, v%d", pDecInsn->VRegA(), pDecInsn->VRegB()); 772 break; 773 case Instruction::k35c: // op {vC, vD, vE, vF, vG}, thing@BBBB 774 // NOT SUPPORTED: 775 // case Instruction::k35ms: // [opt] invoke-virtual+super 776 // case Instruction::k35mi: // [opt] inline invoke 777 { 778 u4 arg[Instruction::kMaxVarArgRegs]; 779 pDecInsn->GetVarArgs(arg); 780 fputs(" {", gOutFile); 781 for (int i = 0, n = pDecInsn->VRegA(); i < n; i++) { 782 if (i == 0) { 783 fprintf(gOutFile, "v%d", arg[i]); 784 } else { 785 fprintf(gOutFile, ", v%d", arg[i]); 786 } 787 } // for 788 fprintf(gOutFile, "}, %s", indexBuf); 789 } 790 break; 791 case Instruction::k25x: // op vC, {vD, vE, vF, vG} (B: count) 792 { 793 u4 arg[Instruction::kMaxVarArgRegs25x]; 794 pDecInsn->GetAllArgs25x(arg); 795 fprintf(gOutFile, " v%d, {", arg[0]); 796 for (int i = 0, n = pDecInsn->VRegB(); i < n; i++) { 797 if (i == 0) { 798 fprintf(gOutFile, "v%d", arg[Instruction::kLambdaVirtualRegisterWidth + i]); 799 } else { 800 fprintf(gOutFile, ", v%d", arg[Instruction::kLambdaVirtualRegisterWidth + i]); 801 } 802 } // for 803 fputc('}', gOutFile); 804 } 805 break; 806 case Instruction::k3rc: // op {vCCCC .. v(CCCC+AA-1)}, thing@BBBB 807 // NOT SUPPORTED: 808 // case Instruction::k3rms: // [opt] invoke-virtual+super/range 809 // case Instruction::k3rmi: // [opt] execute-inline/range 810 { 811 // This doesn't match the "dx" output when some of the args are 812 // 64-bit values -- dx only shows the first register. 813 fputs(" {", gOutFile); 814 for (int i = 0, n = pDecInsn->VRegA(); i < n; i++) { 815 if (i == 0) { 816 fprintf(gOutFile, "v%d", pDecInsn->VRegC() + i); 817 } else { 818 fprintf(gOutFile, ", v%d", pDecInsn->VRegC() + i); 819 } 820 } // for 821 fprintf(gOutFile, "}, %s", indexBuf); 822 } 823 break; 824 case Instruction::k51l: // op vAA, #+BBBBBBBBBBBBBBBB 825 { 826 // This is often, but not always, a double. 827 union { 828 double d; 829 u8 j; 830 } conv; 831 conv.j = pDecInsn->WideVRegB(); 832 fprintf(gOutFile, " v%d, #double %f // #%016" PRIx64, 833 pDecInsn->VRegA(), conv.d, pDecInsn->WideVRegB()); 834 } 835 break; 836 // NOT SUPPORTED: 837 // case Instruction::k00x: // unknown op or breakpoint 838 // break; 839 default: 840 fprintf(gOutFile, " ???"); 841 break; 842 } // switch 843 844 fputc('\n', gOutFile); 845 846 if (indexBuf != indexBufChars) { 847 free(indexBuf); 848 } 849 } 850 851 /* 852 * Dumps a bytecode disassembly. 853 */ 854 static void dumpBytecodes(const DexFile* pDexFile, u4 idx, 855 const DexFile::CodeItem* pCode, u4 codeOffset) { 856 const DexFile::MethodId& pMethodId = pDexFile->GetMethodId(idx); 857 const char* name = pDexFile->StringDataByIdx(pMethodId.name_idx_); 858 const Signature signature = pDexFile->GetMethodSignature(pMethodId); 859 const char* backDescriptor = pDexFile->StringByTypeIdx(pMethodId.class_idx_); 860 861 // Generate header. 862 char* tmp = descriptorToDot(backDescriptor); 863 fprintf(gOutFile, "%06x: " 864 "|[%06x] %s.%s:%s\n", 865 codeOffset, codeOffset, tmp, name, signature.ToString().c_str()); 866 free(tmp); 867 868 // Iterate over all instructions. 869 const u2* insns = pCode->insns_; 870 for (u4 insnIdx = 0; insnIdx < pCode->insns_size_in_code_units_;) { 871 const Instruction* instruction = Instruction::At(&insns[insnIdx]); 872 const u4 insnWidth = instruction->SizeInCodeUnits(); 873 if (insnWidth == 0) { 874 fprintf(stderr, "GLITCH: zero-width instruction at idx=0x%04x\n", insnIdx); 875 break; 876 } 877 dumpInstruction(pDexFile, pCode, codeOffset, insnIdx, insnWidth, instruction); 878 insnIdx += insnWidth; 879 } // for 880 } 881 882 /* 883 * Dumps code of a method. 884 */ 885 static void dumpCode(const DexFile* pDexFile, u4 idx, u4 flags, 886 const DexFile::CodeItem* pCode, u4 codeOffset) { 887 fprintf(gOutFile, " registers : %d\n", pCode->registers_size_); 888 fprintf(gOutFile, " ins : %d\n", pCode->ins_size_); 889 fprintf(gOutFile, " outs : %d\n", pCode->outs_size_); 890 fprintf(gOutFile, " insns size : %d 16-bit code units\n", 891 pCode->insns_size_in_code_units_); 892 893 // Bytecode disassembly, if requested. 894 if (gOptions.disassemble) { 895 dumpBytecodes(pDexFile, idx, pCode, codeOffset); 896 } 897 898 // Try-catch blocks. 899 dumpCatches(pDexFile, pCode); 900 901 // Positions and locals table in the debug info. 902 bool is_static = (flags & kAccStatic) != 0; 903 fprintf(gOutFile, " positions : \n"); 904 pDexFile->DecodeDebugPositionInfo(pCode, dumpPositionsCb, nullptr); 905 fprintf(gOutFile, " locals : \n"); 906 pDexFile->DecodeDebugLocalInfo(pCode, is_static, idx, dumpLocalsCb, nullptr); 907 } 908 909 /* 910 * Dumps a method. 911 */ 912 static void dumpMethod(const DexFile* pDexFile, u4 idx, u4 flags, 913 const DexFile::CodeItem* pCode, u4 codeOffset, int i) { 914 // Bail for anything private if export only requested. 915 if (gOptions.exportsOnly && (flags & (kAccPublic | kAccProtected)) == 0) { 916 return; 917 } 918 919 const DexFile::MethodId& pMethodId = pDexFile->GetMethodId(idx); 920 const char* name = pDexFile->StringDataByIdx(pMethodId.name_idx_); 921 const Signature signature = pDexFile->GetMethodSignature(pMethodId); 922 char* typeDescriptor = strdup(signature.ToString().c_str()); 923 const char* backDescriptor = pDexFile->StringByTypeIdx(pMethodId.class_idx_); 924 char* accessStr = createAccessFlagStr(flags, kAccessForMethod); 925 926 if (gOptions.outputFormat == OUTPUT_PLAIN) { 927 fprintf(gOutFile, " #%d : (in %s)\n", i, backDescriptor); 928 fprintf(gOutFile, " name : '%s'\n", name); 929 fprintf(gOutFile, " type : '%s'\n", typeDescriptor); 930 fprintf(gOutFile, " access : 0x%04x (%s)\n", flags, accessStr); 931 if (pCode == nullptr) { 932 fprintf(gOutFile, " code : (none)\n"); 933 } else { 934 fprintf(gOutFile, " code -\n"); 935 dumpCode(pDexFile, idx, flags, pCode, codeOffset); 936 } 937 if (gOptions.disassemble) { 938 fputc('\n', gOutFile); 939 } 940 } else if (gOptions.outputFormat == OUTPUT_XML) { 941 const bool constructor = (name[0] == '<'); 942 943 // Method name and prototype. 944 if (constructor) { 945 char* tmp = descriptorClassToDot(backDescriptor); 946 fprintf(gOutFile, "<constructor name=\"%s\"\n", tmp); 947 free(tmp); 948 tmp = descriptorToDot(backDescriptor); 949 fprintf(gOutFile, " type=\"%s\"\n", tmp); 950 free(tmp); 951 } else { 952 fprintf(gOutFile, "<method name=\"%s\"\n", name); 953 const char* returnType = strrchr(typeDescriptor, ')'); 954 if (returnType == nullptr) { 955 fprintf(stderr, "bad method type descriptor '%s'\n", typeDescriptor); 956 goto bail; 957 } 958 char* tmp = descriptorToDot(returnType+1); 959 fprintf(gOutFile, " return=\"%s\"\n", tmp); 960 free(tmp); 961 fprintf(gOutFile, " abstract=%s\n", quotedBool((flags & kAccAbstract) != 0)); 962 fprintf(gOutFile, " native=%s\n", quotedBool((flags & kAccNative) != 0)); 963 fprintf(gOutFile, " synchronized=%s\n", quotedBool( 964 (flags & (kAccSynchronized | kAccDeclaredSynchronized)) != 0)); 965 } 966 967 // Additional method flags. 968 fprintf(gOutFile, " static=%s\n", quotedBool((flags & kAccStatic) != 0)); 969 fprintf(gOutFile, " final=%s\n", quotedBool((flags & kAccFinal) != 0)); 970 // The "deprecated=" not knowable w/o parsing annotations. 971 fprintf(gOutFile, " visibility=%s\n>\n", quotedVisibility(flags)); 972 973 // Parameters. 974 if (typeDescriptor[0] != '(') { 975 fprintf(stderr, "ERROR: bad descriptor '%s'\n", typeDescriptor); 976 goto bail; 977 } 978 char* tmpBuf = reinterpret_cast<char*>(malloc(strlen(typeDescriptor) + 1)); 979 const char* base = typeDescriptor + 1; 980 int argNum = 0; 981 while (*base != ')') { 982 char* cp = tmpBuf; 983 while (*base == '[') { 984 *cp++ = *base++; 985 } 986 if (*base == 'L') { 987 // Copy through ';'. 988 do { 989 *cp = *base++; 990 } while (*cp++ != ';'); 991 } else { 992 // Primitive char, copy it. 993 if (strchr("ZBCSIFJD", *base) == NULL) { 994 fprintf(stderr, "ERROR: bad method signature '%s'\n", base); 995 goto bail; 996 } 997 *cp++ = *base++; 998 } 999 // Null terminate and display. 1000 *cp++ = '\0'; 1001 char* tmp = descriptorToDot(tmpBuf); 1002 fprintf(gOutFile, "<parameter name=\"arg%d\" type=\"%s\">\n" 1003 "</parameter>\n", argNum++, tmp); 1004 free(tmp); 1005 } // while 1006 free(tmpBuf); 1007 if (constructor) { 1008 fprintf(gOutFile, "</constructor>\n"); 1009 } else { 1010 fprintf(gOutFile, "</method>\n"); 1011 } 1012 } 1013 1014 bail: 1015 free(typeDescriptor); 1016 free(accessStr); 1017 } 1018 1019 /* 1020 * Dumps a string value with some escape characters. 1021 */ 1022 static void dumpEscapedString(const char* p) { 1023 for (; *p; p++) { 1024 switch (*p) { 1025 case '\\': 1026 fputs("\\\\", gOutFile); 1027 break; 1028 case '\"': 1029 fputs("\\\"", gOutFile); 1030 break; 1031 case '\t': 1032 fputs("\\t", gOutFile); 1033 break; 1034 case '\n': 1035 fputs("\\n", gOutFile); 1036 break; 1037 case '\r': 1038 fputs("\\r", gOutFile); 1039 break; 1040 default: 1041 putc(*p, gOutFile); 1042 } 1043 } 1044 } 1045 1046 /* 1047 * Dumps an XML attribute value between double-quotes. 1048 */ 1049 static void dumpXmlAttribute(const char* p) { 1050 for (; *p; p++) { 1051 switch (*p) { 1052 case '&': 1053 fputs("&", gOutFile); 1054 break; 1055 case '<': 1056 fputs("<", gOutFile); 1057 break; 1058 case '"': 1059 fputs(""", gOutFile); 1060 break; 1061 case '\t': 1062 fputs("	", gOutFile); 1063 break; 1064 case '\n': 1065 fputs("
", gOutFile); 1066 break; 1067 case '\r': 1068 fputs("
", gOutFile); 1069 break; 1070 default: 1071 putc(*p, gOutFile); 1072 } 1073 } 1074 } 1075 1076 /* 1077 * Dumps a value of static (class) field. 1078 */ 1079 static void dumpSFieldValue(const DexFile* pDexFile, 1080 EncodedStaticFieldValueIterator::ValueType valueType, 1081 const jvalue* pValue) { 1082 switch (valueType) { 1083 case EncodedStaticFieldValueIterator::kByte: 1084 fprintf(gOutFile, "%" PRIu8, pValue->b); 1085 break; 1086 case EncodedStaticFieldValueIterator::kShort: 1087 fprintf(gOutFile, "%" PRId16, pValue->s); 1088 break; 1089 case EncodedStaticFieldValueIterator::kChar: 1090 fprintf(gOutFile, "%" PRIu16, pValue->c); 1091 break; 1092 case EncodedStaticFieldValueIterator::kInt: 1093 fprintf(gOutFile, "%" PRId32, pValue->i); 1094 break; 1095 case EncodedStaticFieldValueIterator::kLong: 1096 fprintf(gOutFile, "%" PRId64, pValue->j); 1097 break; 1098 case EncodedStaticFieldValueIterator::kFloat: 1099 fprintf(gOutFile, "%f", pValue->f); 1100 break; 1101 case EncodedStaticFieldValueIterator::kDouble: 1102 fprintf(gOutFile, "%f", pValue->d); 1103 break; 1104 case EncodedStaticFieldValueIterator::kString: { 1105 const char* str = 1106 pDexFile->GetStringData(pDexFile->GetStringId(pValue->i)); 1107 if (gOptions.outputFormat == OUTPUT_PLAIN) { 1108 fputs("\"", gOutFile); 1109 dumpEscapedString(str); 1110 fputs("\"", gOutFile); 1111 } else { 1112 dumpXmlAttribute(str); 1113 } 1114 break; 1115 } 1116 case EncodedStaticFieldValueIterator::kNull: 1117 fputs("null", gOutFile); 1118 break; 1119 case EncodedStaticFieldValueIterator::kBoolean: 1120 fputs(pValue->z ? "true" : "false", gOutFile); 1121 break; 1122 1123 case EncodedStaticFieldValueIterator::kAnnotation: 1124 case EncodedStaticFieldValueIterator::kArray: 1125 case EncodedStaticFieldValueIterator::kEnum: 1126 case EncodedStaticFieldValueIterator::kField: 1127 case EncodedStaticFieldValueIterator::kMethod: 1128 case EncodedStaticFieldValueIterator::kType: 1129 default: 1130 fprintf(gOutFile, "Unexpected static field type: %d", valueType); 1131 } 1132 } 1133 1134 /* 1135 * Dumps a static (class) field. 1136 */ 1137 static void dumpSField(const DexFile* pDexFile, u4 idx, u4 flags, int i, 1138 EncodedStaticFieldValueIterator::ValueType valueType, 1139 const jvalue* pValue) { 1140 // Bail for anything private if export only requested. 1141 if (gOptions.exportsOnly && (flags & (kAccPublic | kAccProtected)) == 0) { 1142 return; 1143 } 1144 1145 const DexFile::FieldId& pFieldId = pDexFile->GetFieldId(idx); 1146 const char* name = pDexFile->StringDataByIdx(pFieldId.name_idx_); 1147 const char* typeDescriptor = pDexFile->StringByTypeIdx(pFieldId.type_idx_); 1148 const char* backDescriptor = pDexFile->StringByTypeIdx(pFieldId.class_idx_); 1149 char* accessStr = createAccessFlagStr(flags, kAccessForField); 1150 1151 if (gOptions.outputFormat == OUTPUT_PLAIN) { 1152 fprintf(gOutFile, " #%d : (in %s)\n", i, backDescriptor); 1153 fprintf(gOutFile, " name : '%s'\n", name); 1154 fprintf(gOutFile, " type : '%s'\n", typeDescriptor); 1155 fprintf(gOutFile, " access : 0x%04x (%s)\n", flags, accessStr); 1156 if (pValue != nullptr) { 1157 fputs(" value : ", gOutFile); 1158 dumpSFieldValue(pDexFile, valueType, pValue); 1159 fputs("\n", gOutFile); 1160 } 1161 } else if (gOptions.outputFormat == OUTPUT_XML) { 1162 fprintf(gOutFile, "<field name=\"%s\"\n", name); 1163 char *tmp = descriptorToDot(typeDescriptor); 1164 fprintf(gOutFile, " type=\"%s\"\n", tmp); 1165 free(tmp); 1166 fprintf(gOutFile, " transient=%s\n", quotedBool((flags & kAccTransient) != 0)); 1167 fprintf(gOutFile, " volatile=%s\n", quotedBool((flags & kAccVolatile) != 0)); 1168 // The "value=" is not knowable w/o parsing annotations. 1169 fprintf(gOutFile, " static=%s\n", quotedBool((flags & kAccStatic) != 0)); 1170 fprintf(gOutFile, " final=%s\n", quotedBool((flags & kAccFinal) != 0)); 1171 // The "deprecated=" is not knowable w/o parsing annotations. 1172 fprintf(gOutFile, " visibility=%s\n", quotedVisibility(flags)); 1173 if (pValue != nullptr) { 1174 fputs(" value=\"", gOutFile); 1175 dumpSFieldValue(pDexFile, valueType, pValue); 1176 fputs("\"\n", gOutFile); 1177 } 1178 fputs(">\n</field>\n", gOutFile); 1179 } 1180 1181 free(accessStr); 1182 } 1183 1184 /* 1185 * Dumps an instance field. 1186 */ 1187 static void dumpIField(const DexFile* pDexFile, u4 idx, u4 flags, int i) { 1188 dumpSField(pDexFile, idx, flags, i, 1189 EncodedStaticFieldValueIterator::kByte, nullptr); 1190 } 1191 1192 /* 1193 * Dumping a CFG. Note that this will do duplicate work. utils.h doesn't expose the code-item 1194 * version, so the DumpMethodCFG code will have to iterate again to find it. But dexdump is a 1195 * tool, so this is not performance-critical. 1196 */ 1197 1198 static void dumpCfg(const DexFile* dex_file, 1199 uint32_t dex_method_idx, 1200 const DexFile::CodeItem* code_item) { 1201 if (code_item != nullptr) { 1202 std::ostringstream oss; 1203 DumpMethodCFG(dex_file, dex_method_idx, oss); 1204 fprintf(gOutFile, "%s", oss.str().c_str()); 1205 } 1206 } 1207 1208 static void dumpCfg(const DexFile* dex_file, int idx) { 1209 const DexFile::ClassDef& class_def = dex_file->GetClassDef(idx); 1210 const uint8_t* class_data = dex_file->GetClassData(class_def); 1211 if (class_data == nullptr) { // empty class such as a marker interface? 1212 return; 1213 } 1214 ClassDataItemIterator it(*dex_file, class_data); 1215 while (it.HasNextStaticField()) { 1216 it.Next(); 1217 } 1218 while (it.HasNextInstanceField()) { 1219 it.Next(); 1220 } 1221 while (it.HasNextDirectMethod()) { 1222 dumpCfg(dex_file, 1223 it.GetMemberIndex(), 1224 it.GetMethodCodeItem()); 1225 it.Next(); 1226 } 1227 while (it.HasNextVirtualMethod()) { 1228 dumpCfg(dex_file, 1229 it.GetMemberIndex(), 1230 it.GetMethodCodeItem()); 1231 it.Next(); 1232 } 1233 } 1234 1235 /* 1236 * Dumps the class. 1237 * 1238 * Note "idx" is a DexClassDef index, not a DexTypeId index. 1239 * 1240 * If "*pLastPackage" is nullptr or does not match the current class' package, 1241 * the value will be replaced with a newly-allocated string. 1242 */ 1243 static void dumpClass(const DexFile* pDexFile, int idx, char** pLastPackage) { 1244 const DexFile::ClassDef& pClassDef = pDexFile->GetClassDef(idx); 1245 1246 // Omitting non-public class. 1247 if (gOptions.exportsOnly && (pClassDef.access_flags_ & kAccPublic) == 0) { 1248 return; 1249 } 1250 1251 if (gOptions.cfg) { 1252 dumpCfg(pDexFile, idx); 1253 return; 1254 } 1255 1256 // For the XML output, show the package name. Ideally we'd gather 1257 // up the classes, sort them, and dump them alphabetically so the 1258 // package name wouldn't jump around, but that's not a great plan 1259 // for something that needs to run on the device. 1260 const char* classDescriptor = pDexFile->StringByTypeIdx(pClassDef.class_idx_); 1261 if (!(classDescriptor[0] == 'L' && 1262 classDescriptor[strlen(classDescriptor)-1] == ';')) { 1263 // Arrays and primitives should not be defined explicitly. Keep going? 1264 fprintf(stderr, "Malformed class name '%s'\n", classDescriptor); 1265 } else if (gOptions.outputFormat == OUTPUT_XML) { 1266 char* mangle = strdup(classDescriptor + 1); 1267 mangle[strlen(mangle)-1] = '\0'; 1268 1269 // Reduce to just the package name. 1270 char* lastSlash = strrchr(mangle, '/'); 1271 if (lastSlash != nullptr) { 1272 *lastSlash = '\0'; 1273 } else { 1274 *mangle = '\0'; 1275 } 1276 1277 for (char* cp = mangle; *cp != '\0'; cp++) { 1278 if (*cp == '/') { 1279 *cp = '.'; 1280 } 1281 } // for 1282 1283 if (*pLastPackage == nullptr || strcmp(mangle, *pLastPackage) != 0) { 1284 // Start of a new package. 1285 if (*pLastPackage != nullptr) { 1286 fprintf(gOutFile, "</package>\n"); 1287 } 1288 fprintf(gOutFile, "<package name=\"%s\"\n>\n", mangle); 1289 free(*pLastPackage); 1290 *pLastPackage = mangle; 1291 } else { 1292 free(mangle); 1293 } 1294 } 1295 1296 // General class information. 1297 char* accessStr = createAccessFlagStr(pClassDef.access_flags_, kAccessForClass); 1298 const char* superclassDescriptor; 1299 if (pClassDef.superclass_idx_ == DexFile::kDexNoIndex16) { 1300 superclassDescriptor = nullptr; 1301 } else { 1302 superclassDescriptor = pDexFile->StringByTypeIdx(pClassDef.superclass_idx_); 1303 } 1304 if (gOptions.outputFormat == OUTPUT_PLAIN) { 1305 fprintf(gOutFile, "Class #%d -\n", idx); 1306 fprintf(gOutFile, " Class descriptor : '%s'\n", classDescriptor); 1307 fprintf(gOutFile, " Access flags : 0x%04x (%s)\n", pClassDef.access_flags_, accessStr); 1308 if (superclassDescriptor != nullptr) { 1309 fprintf(gOutFile, " Superclass : '%s'\n", superclassDescriptor); 1310 } 1311 fprintf(gOutFile, " Interfaces -\n"); 1312 } else { 1313 char* tmp = descriptorClassToDot(classDescriptor); 1314 fprintf(gOutFile, "<class name=\"%s\"\n", tmp); 1315 free(tmp); 1316 if (superclassDescriptor != nullptr) { 1317 tmp = descriptorToDot(superclassDescriptor); 1318 fprintf(gOutFile, " extends=\"%s\"\n", tmp); 1319 free(tmp); 1320 } 1321 fprintf(gOutFile, " interface=%s\n", 1322 quotedBool((pClassDef.access_flags_ & kAccInterface) != 0)); 1323 fprintf(gOutFile, " abstract=%s\n", quotedBool((pClassDef.access_flags_ & kAccAbstract) != 0)); 1324 fprintf(gOutFile, " static=%s\n", quotedBool((pClassDef.access_flags_ & kAccStatic) != 0)); 1325 fprintf(gOutFile, " final=%s\n", quotedBool((pClassDef.access_flags_ & kAccFinal) != 0)); 1326 // The "deprecated=" not knowable w/o parsing annotations. 1327 fprintf(gOutFile, " visibility=%s\n", quotedVisibility(pClassDef.access_flags_)); 1328 fprintf(gOutFile, ">\n"); 1329 } 1330 1331 // Interfaces. 1332 const DexFile::TypeList* pInterfaces = pDexFile->GetInterfacesList(pClassDef); 1333 if (pInterfaces != nullptr) { 1334 for (u4 i = 0; i < pInterfaces->Size(); i++) { 1335 dumpInterface(pDexFile, pInterfaces->GetTypeItem(i), i); 1336 } // for 1337 } 1338 1339 // Fields and methods. 1340 const u1* pEncodedData = pDexFile->GetClassData(pClassDef); 1341 if (pEncodedData == nullptr) { 1342 if (gOptions.outputFormat == OUTPUT_PLAIN) { 1343 fprintf(gOutFile, " Static fields -\n"); 1344 fprintf(gOutFile, " Instance fields -\n"); 1345 fprintf(gOutFile, " Direct methods -\n"); 1346 fprintf(gOutFile, " Virtual methods -\n"); 1347 } 1348 } else { 1349 ClassDataItemIterator pClassData(*pDexFile, pEncodedData); 1350 if (gOptions.outputFormat == OUTPUT_PLAIN) { 1351 fprintf(gOutFile, " Static fields -\n"); 1352 } 1353 EncodedStaticFieldValueIterator staticFieldValues(*pDexFile, pClassDef); 1354 for (int i = 0; pClassData.HasNextStaticField(); i++, pClassData.Next()) { 1355 EncodedStaticFieldValueIterator::ValueType valueType = 1356 EncodedStaticFieldValueIterator::kByte; 1357 const jvalue* pValue = nullptr; 1358 if (staticFieldValues.HasNext()) { 1359 valueType = staticFieldValues.GetValueType(); 1360 pValue = &staticFieldValues.GetJavaValue(); 1361 } 1362 dumpSField(pDexFile, pClassData.GetMemberIndex(), 1363 pClassData.GetRawMemberAccessFlags(), i, 1364 valueType, pValue); 1365 if (staticFieldValues.HasNext()) { 1366 staticFieldValues.Next(); 1367 } 1368 } // for 1369 DCHECK(!staticFieldValues.HasNext()); 1370 if (gOptions.outputFormat == OUTPUT_PLAIN) { 1371 fprintf(gOutFile, " Instance fields -\n"); 1372 } 1373 for (int i = 0; pClassData.HasNextInstanceField(); i++, pClassData.Next()) { 1374 dumpIField(pDexFile, pClassData.GetMemberIndex(), 1375 pClassData.GetRawMemberAccessFlags(), i); 1376 } // for 1377 if (gOptions.outputFormat == OUTPUT_PLAIN) { 1378 fprintf(gOutFile, " Direct methods -\n"); 1379 } 1380 for (int i = 0; pClassData.HasNextDirectMethod(); i++, pClassData.Next()) { 1381 dumpMethod(pDexFile, pClassData.GetMemberIndex(), 1382 pClassData.GetRawMemberAccessFlags(), 1383 pClassData.GetMethodCodeItem(), 1384 pClassData.GetMethodCodeItemOffset(), i); 1385 } // for 1386 if (gOptions.outputFormat == OUTPUT_PLAIN) { 1387 fprintf(gOutFile, " Virtual methods -\n"); 1388 } 1389 for (int i = 0; pClassData.HasNextVirtualMethod(); i++, pClassData.Next()) { 1390 dumpMethod(pDexFile, pClassData.GetMemberIndex(), 1391 pClassData.GetRawMemberAccessFlags(), 1392 pClassData.GetMethodCodeItem(), 1393 pClassData.GetMethodCodeItemOffset(), i); 1394 } // for 1395 } 1396 1397 // End of class. 1398 if (gOptions.outputFormat == OUTPUT_PLAIN) { 1399 const char* fileName; 1400 if (pClassDef.source_file_idx_ != DexFile::kDexNoIndex) { 1401 fileName = pDexFile->StringDataByIdx(pClassDef.source_file_idx_); 1402 } else { 1403 fileName = "unknown"; 1404 } 1405 fprintf(gOutFile, " source_file_idx : %d (%s)\n\n", 1406 pClassDef.source_file_idx_, fileName); 1407 } else if (gOptions.outputFormat == OUTPUT_XML) { 1408 fprintf(gOutFile, "</class>\n"); 1409 } 1410 1411 free(accessStr); 1412 } 1413 1414 /* 1415 * Dumps the requested sections of the file. 1416 */ 1417 static void processDexFile(const char* fileName, const DexFile* pDexFile) { 1418 if (gOptions.verbose) { 1419 fprintf(gOutFile, "Opened '%s', DEX version '%.3s'\n", 1420 fileName, pDexFile->GetHeader().magic_ + 4); 1421 } 1422 1423 // Headers. 1424 if (gOptions.showFileHeaders) { 1425 dumpFileHeader(pDexFile); 1426 } 1427 1428 // Open XML context. 1429 if (gOptions.outputFormat == OUTPUT_XML) { 1430 fprintf(gOutFile, "<api>\n"); 1431 } 1432 1433 // Iterate over all classes. 1434 char* package = nullptr; 1435 const u4 classDefsSize = pDexFile->GetHeader().class_defs_size_; 1436 for (u4 i = 0; i < classDefsSize; i++) { 1437 if (gOptions.showSectionHeaders) { 1438 dumpClassDef(pDexFile, i); 1439 } 1440 dumpClass(pDexFile, i, &package); 1441 } // for 1442 1443 // Free the last package allocated. 1444 if (package != nullptr) { 1445 fprintf(gOutFile, "</package>\n"); 1446 free(package); 1447 } 1448 1449 // Close XML context. 1450 if (gOptions.outputFormat == OUTPUT_XML) { 1451 fprintf(gOutFile, "</api>\n"); 1452 } 1453 } 1454 1455 /* 1456 * Processes a single file (either direct .dex or indirect .zip/.jar/.apk). 1457 */ 1458 int processFile(const char* fileName) { 1459 if (gOptions.verbose) { 1460 fprintf(gOutFile, "Processing '%s'...\n", fileName); 1461 } 1462 1463 // If the file is not a .dex file, the function tries .zip/.jar/.apk files, 1464 // all of which are Zip archives with "classes.dex" inside. The compressed 1465 // data needs to be extracted to a temp file, the location of which varies. 1466 // 1467 // TODO(ajcbik): fix following issues 1468 // 1469 // (1) gOptions.tempFileName is not accounted for 1470 // (2) gOptions.ignoreBadChecksum is not accounted for 1471 // 1472 std::string error_msg; 1473 std::vector<std::unique_ptr<const DexFile>> dex_files; 1474 if (!DexFile::Open(fileName, fileName, &error_msg, &dex_files)) { 1475 // Display returned error message to user. Note that this error behavior 1476 // differs from the error messages shown by the original Dalvik dexdump. 1477 fputs(error_msg.c_str(), stderr); 1478 fputc('\n', stderr); 1479 return -1; 1480 } 1481 1482 // Success. Either report checksum verification or process 1483 // all dex files found in given file. 1484 if (gOptions.checksumOnly) { 1485 fprintf(gOutFile, "Checksum verified\n"); 1486 } else { 1487 for (size_t i = 0; i < dex_files.size(); i++) { 1488 processDexFile(fileName, dex_files[i].get()); 1489 } 1490 } 1491 return 0; 1492 } 1493 1494 } // namespace art 1495