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