• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
get2LE(unsigned char const * pSrc)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  */
primitiveTypeLabel(char typeChar)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  */
descriptorToDot(const char * str)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  */
descriptorClassToDot(const char * str)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  */
quotedBool(bool val)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  */
quotedVisibility(u4 accessFlags)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  */
countOnes(u4 val)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  */
createAccessFlagStr(u4 flags,AccessFor forWhat)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  */
asciify(char * out,const unsigned char * data,size_t len)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  */
dumpFileHeader(const DexFile * pDexFile)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  */
dumpClassDef(const DexFile * pDexFile,int idx)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  */
dumpInterface(const DexFile * pDexFile,const DexFile::TypeItem & pTypeItem,int i)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  */
dumpCatches(const DexFile * pDexFile,const DexFile::CodeItem * pCode)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  */
dumpPositionsCb(void *,const DexFile::PositionInfo & entry)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  */
dumpLocalsCb(void *,const DexFile::LocalInfo & entry)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  */
indexString(const DexFile * pDexFile,const Instruction * pDecInsn,char * buf,size_t bufSize)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  */
dumpInstruction(const DexFile * pDexFile,const DexFile::CodeItem * pCode,u4 codeOffset,u4 insnIdx,u4 insnWidth,const Instruction * pDecInsn)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  */
dumpBytecodes(const DexFile * pDexFile,u4 idx,const DexFile::CodeItem * pCode,u4 codeOffset)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  */
dumpCode(const DexFile * pDexFile,u4 idx,u4 flags,const DexFile::CodeItem * pCode,u4 codeOffset)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  */
dumpMethod(const DexFile * pDexFile,u4 idx,u4 flags,const DexFile::CodeItem * pCode,u4 codeOffset,int i)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  */
dumpEscapedString(const char * p)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  */
dumpXmlAttribute(const char * p)1049 static void dumpXmlAttribute(const char* p) {
1050   for (; *p; p++) {
1051     switch (*p) {
1052       case '&':
1053         fputs("&amp;", gOutFile);
1054         break;
1055       case '<':
1056         fputs("&lt;", gOutFile);
1057         break;
1058       case '"':
1059         fputs("&quot;", gOutFile);
1060         break;
1061       case '\t':
1062         fputs("&#x9;", gOutFile);
1063         break;
1064       case '\n':
1065         fputs("&#xA;", gOutFile);
1066         break;
1067       case '\r':
1068         fputs("&#xD;", gOutFile);
1069         break;
1070       default:
1071         putc(*p, gOutFile);
1072     }
1073   }
1074 }
1075 
1076 /*
1077  * Dumps a value of static (class) field.
1078  */
dumpSFieldValue(const DexFile * pDexFile,EncodedStaticFieldValueIterator::ValueType valueType,const jvalue * pValue)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  */
dumpSField(const DexFile * pDexFile,u4 idx,u4 flags,int i,EncodedStaticFieldValueIterator::ValueType valueType,const jvalue * pValue)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  */
dumpIField(const DexFile * pDexFile,u4 idx,u4 flags,int i)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 
dumpCfg(const DexFile * dex_file,uint32_t dex_method_idx,const DexFile::CodeItem * code_item)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 
dumpCfg(const DexFile * dex_file,int idx)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  */
dumpClass(const DexFile * pDexFile,int idx,char ** pLastPackage)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  */
processDexFile(const char * fileName,const DexFile * pDexFile)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  */
processFile(const char * fileName)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