• 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("field_ids_size      : %d\n", pHeader->fieldIdsSize);
439     printf("field_ids_off       : %d (0x%06x)\n",
440         pHeader->fieldIdsOff, pHeader->fieldIdsOff);
441     printf("method_ids_size     : %d\n", pHeader->methodIdsSize);
442     printf("method_ids_off      : %d (0x%06x)\n",
443         pHeader->methodIdsOff, pHeader->methodIdsOff);
444     printf("class_defs_size     : %d\n", pHeader->classDefsSize);
445     printf("class_defs_off      : %d (0x%06x)\n",
446         pHeader->classDefsOff, pHeader->classDefsOff);
447     printf("data_size           : %d\n", pHeader->dataSize);
448     printf("data_off            : %d (0x%06x)\n",
449         pHeader->dataOff, pHeader->dataOff);
450     printf("\n");
451 }
452 
453 /*
454  * Dump the "table of contents" for the opt area.
455  */
dumpOptDirectory(const DexFile * pDexFile)456 void dumpOptDirectory(const DexFile* pDexFile)
457 {
458     const DexOptHeader* pOptHeader = pDexFile->pOptHeader;
459     if (pOptHeader == NULL)
460         return;
461 
462     printf("OPT section contents:\n");
463 
464     const u4* pOpt = (const u4*) ((u1*) pOptHeader + pOptHeader->optOffset);
465 
466     if (*pOpt == 0) {
467         printf("(1.0 format, only class lookup table is present)\n\n");
468         return;
469     }
470 
471     /*
472      * The "opt" section is in "chunk" format: a 32-bit identifier, a 32-bit
473      * length, then the data.  Chunks start on 64-bit boundaries.
474      */
475     while (*pOpt != kDexChunkEnd) {
476         const char* verboseStr;
477 
478         u4 size = *(pOpt+1);
479 
480         switch (*pOpt) {
481         case kDexChunkClassLookup:
482             verboseStr = "class lookup hash table";
483             break;
484         case kDexChunkRegisterMaps:
485             verboseStr = "register maps";
486             break;
487         default:
488             verboseStr = "(unknown chunk type)";
489             break;
490         }
491 
492         printf("Chunk %08x (%c%c%c%c) - %s (%d bytes)\n", *pOpt,
493             *pOpt >> 24, (char)(*pOpt >> 16), (char)(*pOpt >> 8), (char)*pOpt,
494             verboseStr, size);
495 
496         size = (size + 8 + 7) & ~7;
497         pOpt += size / sizeof(u4);
498     }
499     printf("\n");
500 }
501 
502 /*
503  * Dump a class_def_item.
504  */
dumpClassDef(DexFile * pDexFile,int idx)505 void dumpClassDef(DexFile* pDexFile, int idx)
506 {
507     const DexClassDef* pClassDef;
508     const u1* pEncodedData;
509     DexClassData* pClassData;
510 
511     pClassDef = dexGetClassDef(pDexFile, idx);
512     pEncodedData = dexGetClassData(pDexFile, pClassDef);
513     pClassData = dexReadAndVerifyClassData(&pEncodedData, NULL);
514 
515     if (pClassData == NULL) {
516         fprintf(stderr, "Trouble reading class data\n");
517         return;
518     }
519 
520     printf("Class #%d header:\n", idx);
521     printf("class_idx           : %d\n", pClassDef->classIdx);
522     printf("access_flags        : %d (0x%04x)\n",
523         pClassDef->accessFlags, pClassDef->accessFlags);
524     printf("superclass_idx      : %d\n", pClassDef->superclassIdx);
525     printf("interfaces_off      : %d (0x%06x)\n",
526         pClassDef->interfacesOff, pClassDef->interfacesOff);
527     printf("source_file_idx     : %d\n", pClassDef->sourceFileIdx);
528     printf("annotations_off     : %d (0x%06x)\n",
529         pClassDef->annotationsOff, pClassDef->annotationsOff);
530     printf("class_data_off      : %d (0x%06x)\n",
531         pClassDef->classDataOff, pClassDef->classDataOff);
532     printf("static_fields_size  : %d\n", pClassData->header.staticFieldsSize);
533     printf("instance_fields_size: %d\n",
534             pClassData->header.instanceFieldsSize);
535     printf("direct_methods_size : %d\n", pClassData->header.directMethodsSize);
536     printf("virtual_methods_size: %d\n",
537             pClassData->header.virtualMethodsSize);
538     printf("\n");
539 
540     free(pClassData);
541 }
542 
543 /*
544  * Dump an interface that a class declares to implement.
545  */
dumpInterface(const DexFile * pDexFile,const DexTypeItem * pTypeItem,int i)546 void dumpInterface(const DexFile* pDexFile, const DexTypeItem* pTypeItem,
547     int i)
548 {
549     const char* interfaceName =
550         dexStringByTypeIdx(pDexFile, pTypeItem->typeIdx);
551 
552     if (gOptions.outputFormat == OUTPUT_PLAIN) {
553         printf("    #%d              : '%s'\n", i, interfaceName);
554     } else {
555         char* dotted = descriptorToDot(interfaceName);
556         printf("<implements name=\"%s\">\n</implements>\n", dotted);
557         free(dotted);
558     }
559 }
560 
561 /*
562  * Dump the catches table associated with the code.
563  */
dumpCatches(DexFile * pDexFile,const DexCode * pCode)564 void dumpCatches(DexFile* pDexFile, const DexCode* pCode)
565 {
566     u4 triesSize = pCode->triesSize;
567 
568     if (triesSize == 0) {
569         printf("      catches       : (none)\n");
570         return;
571     }
572 
573     printf("      catches       : %d\n", triesSize);
574 
575     const DexTry* pTries = dexGetTries(pCode);
576     u4 i;
577 
578     for (i = 0; i < triesSize; i++) {
579         const DexTry* pTry = &pTries[i];
580         u4 start = pTry->startAddr;
581         u4 end = start + pTry->insnCount;
582         DexCatchIterator iterator;
583 
584         printf("        0x%04x - 0x%04x\n", start, end);
585 
586         dexCatchIteratorInit(&iterator, pCode, pTry->handlerOff);
587 
588         for (;;) {
589             DexCatchHandler* handler = dexCatchIteratorNext(&iterator);
590             const char* descriptor;
591 
592             if (handler == NULL) {
593                 break;
594             }
595 
596             descriptor = (handler->typeIdx == kDexNoIndex) ? "<any>" :
597                 dexStringByTypeIdx(pDexFile, handler->typeIdx);
598 
599             printf("          %s -> 0x%04x\n", descriptor,
600                     handler->address);
601         }
602     }
603 }
604 
dumpPositionsCb(void * cnxt,u4 address,u4 lineNum)605 static int dumpPositionsCb(void *cnxt, u4 address, u4 lineNum)
606 {
607     printf("        0x%04x line=%d\n", address, lineNum);
608     return 0;
609 }
610 
611 /*
612  * Dump the positions list.
613  */
dumpPositions(DexFile * pDexFile,const DexCode * pCode,const DexMethod * pDexMethod)614 void dumpPositions(DexFile* pDexFile, const DexCode* pCode,
615         const DexMethod *pDexMethod)
616 {
617     printf("      positions     : \n");
618     const DexMethodId *pMethodId
619             = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
620     const char *classDescriptor
621             = dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
622 
623     dexDecodeDebugInfo(pDexFile, pCode, classDescriptor, pMethodId->protoIdx,
624             pDexMethod->accessFlags, dumpPositionsCb, NULL, NULL);
625 }
626 
dumpLocalsCb(void * cnxt,u2 reg,u4 startAddress,u4 endAddress,const char * name,const char * descriptor,const char * signature)627 static void dumpLocalsCb(void *cnxt, u2 reg, u4 startAddress,
628         u4 endAddress, const char *name, const char *descriptor,
629         const char *signature)
630 {
631     printf("        0x%04x - 0x%04x reg=%d %s %s %s\n",
632             startAddress, endAddress, reg, name, descriptor,
633             signature);
634 }
635 
636 /*
637  * Dump the locals list.
638  */
dumpLocals(DexFile * pDexFile,const DexCode * pCode,const DexMethod * pDexMethod)639 void dumpLocals(DexFile* pDexFile, const DexCode* pCode,
640         const DexMethod *pDexMethod)
641 {
642     printf("      locals        : \n");
643 
644     const DexMethodId *pMethodId
645             = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
646     const char *classDescriptor
647             = dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
648 
649     dexDecodeDebugInfo(pDexFile, pCode, classDescriptor, pMethodId->protoIdx,
650             pDexMethod->accessFlags, NULL, dumpLocalsCb, NULL);
651 }
652 
653 /*
654  * Get information about a method.
655  */
getMethodInfo(DexFile * pDexFile,u4 methodIdx,FieldMethodInfo * pMethInfo)656 bool getMethodInfo(DexFile* pDexFile, u4 methodIdx, FieldMethodInfo* pMethInfo)
657 {
658     const DexMethodId* pMethodId;
659 
660     if (methodIdx >= pDexFile->pHeader->methodIdsSize)
661         return false;
662 
663     pMethodId = dexGetMethodId(pDexFile, methodIdx);
664     pMethInfo->name = dexStringById(pDexFile, pMethodId->nameIdx);
665     pMethInfo->signature = dexCopyDescriptorFromMethodId(pDexFile, pMethodId);
666 
667     pMethInfo->classDescriptor =
668             dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
669     return true;
670 }
671 
672 /*
673  * Get information about a field.
674  */
getFieldInfo(DexFile * pDexFile,u4 fieldIdx,FieldMethodInfo * pFieldInfo)675 bool getFieldInfo(DexFile* pDexFile, u4 fieldIdx, FieldMethodInfo* pFieldInfo)
676 {
677     const DexFieldId* pFieldId;
678 
679     if (fieldIdx >= pDexFile->pHeader->fieldIdsSize)
680         return false;
681 
682     pFieldId = dexGetFieldId(pDexFile, fieldIdx);
683     pFieldInfo->name = dexStringById(pDexFile, pFieldId->nameIdx);
684     pFieldInfo->signature = dexStringByTypeIdx(pDexFile, pFieldId->typeIdx);
685     pFieldInfo->classDescriptor =
686         dexStringByTypeIdx(pDexFile, pFieldId->classIdx);
687     return true;
688 }
689 
690 
691 /*
692  * Look up a class' descriptor.
693  */
getClassDescriptor(DexFile * pDexFile,u4 classIdx)694 const char* getClassDescriptor(DexFile* pDexFile, u4 classIdx)
695 {
696     return dexStringByTypeIdx(pDexFile, classIdx);
697 }
698 
699 /*
700  * Helper for dumpInstruction(), which builds the string
701  * representation for the index in the given instruction. This will
702  * first try to use the given buffer, but if the result won't fit,
703  * then this will allocate a new buffer to hold the result. A pointer
704  * to the buffer which holds the full result is always returned, and
705  * this can be compared with the one passed in, to see if the result
706  * needs to be free()d.
707  */
indexString(DexFile * pDexFile,const DecodedInstruction * pDecInsn,char * buf,size_t bufSize)708 static char* indexString(DexFile* pDexFile,
709     const DecodedInstruction* pDecInsn, char* buf, size_t bufSize)
710 {
711     int outSize;
712     u4 index;
713     u4 width;
714 
715     /* TODO: Make the index *always* be in field B, to simplify this code. */
716     switch (dexGetFormatFromOpcode(pDecInsn->opcode)) {
717     case kFmt20bc:
718     case kFmt21c:
719     case kFmt35c:
720     case kFmt35ms:
721     case kFmt3rc:
722     case kFmt3rms:
723     case kFmt35mi:
724     case kFmt3rmi:
725         index = pDecInsn->vB;
726         width = 4;
727         break;
728     case kFmt31c:
729     case kFmt40sc:
730     case kFmt41c:
731     case kFmt5rc:
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     case kFmt52c:
741         index = pDecInsn->vC;
742         width = 8;
743         break;
744     default:
745         index = 0;
746         width = 4;
747         break;
748     }
749 
750     switch (pDecInsn->indexType) {
751     case kIndexUnknown:
752         /*
753          * This function shouldn't ever get called for this type, but do
754          * something sensible here, just to help with debugging.
755          */
756         outSize = snprintf(buf, bufSize, "<unknown-index>");
757         break;
758     case kIndexNone:
759         /*
760          * This function shouldn't ever get called for this type, but do
761          * something sensible here, just to help with debugging.
762          */
763         outSize = snprintf(buf, bufSize, "<no-index>");
764         break;
765     case kIndexVaries:
766         /*
767          * This one should never show up in a dexdump, so no need to try
768          * to get fancy here.
769          */
770         outSize = snprintf(buf, bufSize, "<index-varies> // thing@%0*x",
771                 width, index);
772         break;
773     case kIndexTypeRef:
774         outSize = snprintf(buf, bufSize, "%s // type@%0*x",
775                 getClassDescriptor(pDexFile, index), width, index);
776         break;
777     case kIndexStringRef:
778         outSize = snprintf(buf, bufSize, "\"%s\" // string@%0*x",
779                 dexStringById(pDexFile, index), width, index);
780         break;
781     case kIndexMethodRef:
782         {
783             FieldMethodInfo methInfo;
784             if (getMethodInfo(pDexFile, index, &methInfo)) {
785                 outSize = snprintf(buf, bufSize, "%s.%s:%s // method@%0*x",
786                         methInfo.classDescriptor, methInfo.name,
787                         methInfo.signature, width, index);
788             } else {
789                 outSize = snprintf(buf, bufSize, "<method?> // method@%0*x",
790                         width, index);
791             }
792         }
793         break;
794     case kIndexFieldRef:
795         {
796             FieldMethodInfo fieldInfo;
797             if (getFieldInfo(pDexFile, index, &fieldInfo)) {
798                 outSize = snprintf(buf, bufSize, "%s.%s:%s // field@%0*x",
799                         fieldInfo.classDescriptor, fieldInfo.name,
800                         fieldInfo.signature, width, index);
801             } else {
802                 outSize = snprintf(buf, bufSize, "<field?> // field@%0*x",
803                         width, index);
804             }
805         }
806         break;
807     case kIndexInlineMethod:
808         outSize = snprintf(buf, bufSize, "[%0*x] // inline #%0*x",
809                 width, index, width, index);
810         break;
811     case kIndexVtableOffset:
812         outSize = snprintf(buf, bufSize, "[%0*x] // vtable #%0*x",
813                 width, index, width, index);
814         break;
815     case kIndexFieldOffset:
816         outSize = snprintf(buf, bufSize, "[obj+%0*x]", width, index);
817         break;
818     default:
819         outSize = snprintf(buf, bufSize, "<?>");
820         break;
821     }
822 
823     if (outSize >= (int) bufSize) {
824         /*
825          * The buffer wasn't big enough; allocate and retry. Note:
826          * snprintf() doesn't count the '\0' as part of its returned
827          * size, so we add explicit space for it here.
828          */
829         outSize++;
830         buf = (char*)malloc(outSize);
831         if (buf == NULL) {
832             return NULL;
833         }
834         return indexString(pDexFile, pDecInsn, buf, outSize);
835     } else {
836         return buf;
837     }
838 }
839 
840 /*
841  * Dump a single instruction.
842  */
dumpInstruction(DexFile * pDexFile,const DexCode * pCode,int insnIdx,int insnWidth,const DecodedInstruction * pDecInsn)843 void dumpInstruction(DexFile* pDexFile, const DexCode* pCode, int insnIdx,
844     int insnWidth, const DecodedInstruction* pDecInsn)
845 {
846     char indexBufChars[200];
847     char *indexBuf = indexBufChars;
848     const u2* insns = pCode->insns;
849     int i;
850 
851     printf("%06x:", ((u1*)insns - pDexFile->baseAddr) + insnIdx*2);
852     for (i = 0; i < 8; i++) {
853         if (i < insnWidth) {
854             if (i == 7) {
855                 printf(" ... ");
856             } else {
857                 /* print 16-bit value in little-endian order */
858                 const u1* bytePtr = (const u1*) &insns[insnIdx+i];
859                 printf(" %02x%02x", bytePtr[0], bytePtr[1]);
860             }
861         } else {
862             fputs("     ", stdout);
863         }
864     }
865 
866     if (pDecInsn->opcode == OP_NOP) {
867         u2 instr = get2LE((const u1*) &insns[insnIdx]);
868         if (instr == kPackedSwitchSignature) {
869             printf("|%04x: packed-switch-data (%d units)",
870                 insnIdx, insnWidth);
871         } else if (instr == kSparseSwitchSignature) {
872             printf("|%04x: sparse-switch-data (%d units)",
873                 insnIdx, insnWidth);
874         } else if (instr == kArrayDataSignature) {
875             printf("|%04x: array-data (%d units)",
876                 insnIdx, insnWidth);
877         } else {
878             printf("|%04x: nop // spacer", insnIdx);
879         }
880     } else {
881         printf("|%04x: %s", insnIdx, dexGetOpcodeName(pDecInsn->opcode));
882     }
883 
884     if (pDecInsn->indexType != kIndexNone) {
885         indexBuf = indexString(pDexFile, pDecInsn,
886                 indexBufChars, sizeof(indexBufChars));
887     }
888 
889     switch (dexGetFormatFromOpcode(pDecInsn->opcode)) {
890     case kFmt10x:        // op
891         break;
892     case kFmt12x:        // op vA, vB
893         printf(" v%d, v%d", pDecInsn->vA, pDecInsn->vB);
894         break;
895     case kFmt11n:        // op vA, #+B
896         printf(" v%d, #int %d // #%x",
897             pDecInsn->vA, (s4)pDecInsn->vB, (u1)pDecInsn->vB);
898         break;
899     case kFmt11x:        // op vAA
900         printf(" v%d", pDecInsn->vA);
901         break;
902     case kFmt10t:        // op +AA
903     case kFmt20t:        // op +AAAA
904         {
905             s4 targ = (s4) pDecInsn->vA;
906             printf(" %04x // %c%04x",
907                 insnIdx + targ,
908                 (targ < 0) ? '-' : '+',
909                 (targ < 0) ? -targ : targ);
910         }
911         break;
912     case kFmt22x:        // op vAA, vBBBB
913         printf(" v%d, v%d", pDecInsn->vA, pDecInsn->vB);
914         break;
915     case kFmt21t:        // op vAA, +BBBB
916         {
917             s4 targ = (s4) pDecInsn->vB;
918             printf(" v%d, %04x // %c%04x", pDecInsn->vA,
919                 insnIdx + targ,
920                 (targ < 0) ? '-' : '+',
921                 (targ < 0) ? -targ : targ);
922         }
923         break;
924     case kFmt21s:        // op vAA, #+BBBB
925         printf(" v%d, #int %d // #%x",
926             pDecInsn->vA, (s4)pDecInsn->vB, (u2)pDecInsn->vB);
927         break;
928     case kFmt21h:        // op vAA, #+BBBB0000[00000000]
929         // The printed format varies a bit based on the actual opcode.
930         if (pDecInsn->opcode == OP_CONST_HIGH16) {
931             s4 value = pDecInsn->vB << 16;
932             printf(" v%d, #int %d // #%x",
933                 pDecInsn->vA, value, (u2)pDecInsn->vB);
934         } else {
935             s8 value = ((s8) pDecInsn->vB) << 48;
936             printf(" v%d, #long %lld // #%x",
937                 pDecInsn->vA, value, (u2)pDecInsn->vB);
938         }
939         break;
940     case kFmt21c:        // op vAA, thing@BBBB
941     case kFmt31c:        // op vAA, thing@BBBBBBBB
942     case kFmt41c:        // exop vAAAA, thing@BBBBBBBB
943         printf(" v%d, %s", pDecInsn->vA, indexBuf);
944         break;
945     case kFmt23x:        // op vAA, vBB, vCC
946     case kFmt33x:        // exop vAA, vBB, vCCCC
947         printf(" v%d, v%d, v%d", pDecInsn->vA, pDecInsn->vB, pDecInsn->vC);
948         break;
949     case kFmt22b:        // op vAA, vBB, #+CC
950         printf(" v%d, v%d, #int %d // #%02x",
951             pDecInsn->vA, pDecInsn->vB, (s4)pDecInsn->vC, (u1)pDecInsn->vC);
952         break;
953     case kFmt22t:        // op vA, vB, +CCCC
954         {
955             s4 targ = (s4) pDecInsn->vC;
956             printf(" v%d, v%d, %04x // %c%04x", pDecInsn->vA, pDecInsn->vB,
957                 insnIdx + targ,
958                 (targ < 0) ? '-' : '+',
959                 (targ < 0) ? -targ : targ);
960         }
961         break;
962     case kFmt22s:        // op vA, vB, #+CCCC
963     case kFmt32s:        // exop vAA, vBB, #+CCCC
964         printf(" v%d, v%d, #int %d // #%04x",
965             pDecInsn->vA, pDecInsn->vB, (s4)pDecInsn->vC, (u2)pDecInsn->vC);
966         break;
967     case kFmt22c:        // op vA, vB, thing@CCCC
968     case kFmt22cs:       // [opt] op vA, vB, field offset CCCC
969     case kFmt52c:        // exop vAAAA, vBBBB, thing@CCCCCCCC
970         printf(" v%d, v%d, %s", pDecInsn->vA, pDecInsn->vB, indexBuf);
971         break;
972     case kFmt30t:
973         printf(" #%08x", pDecInsn->vA);
974         break;
975     case kFmt31i:        // op vAA, #+BBBBBBBB
976         {
977             /* this is often, but not always, a float */
978             union {
979                 float f;
980                 u4 i;
981             } conv;
982             conv.i = pDecInsn->vB;
983             printf(" v%d, #float %f // #%08x",
984                 pDecInsn->vA, conv.f, pDecInsn->vB);
985         }
986         break;
987     case kFmt31t:       // op vAA, offset +BBBBBBBB
988         printf(" v%d, %08x // +%08x",
989             pDecInsn->vA, insnIdx + pDecInsn->vB, pDecInsn->vB);
990         break;
991     case kFmt32x:        // op vAAAA, vBBBB
992         printf(" v%d, v%d", pDecInsn->vA, pDecInsn->vB);
993         break;
994     case kFmt35c:        // op {vC, vD, vE, vF, vG}, thing@BBBB
995     case kFmt35ms:       // [opt] invoke-virtual+super
996     case kFmt35mi:       // [opt] inline invoke
997         {
998             fputs(" {", stdout);
999             for (i = 0; i < (int) pDecInsn->vA; i++) {
1000                 if (i == 0)
1001                     printf("v%d", pDecInsn->arg[i]);
1002                 else
1003                     printf(", v%d", pDecInsn->arg[i]);
1004             }
1005             printf("}, %s", indexBuf);
1006         }
1007         break;
1008     case kFmt3rc:        // op {vCCCC .. v(CCCC+AA-1)}, thing@BBBB
1009     case kFmt3rms:       // [opt] invoke-virtual+super/range
1010     case kFmt3rmi:       // [opt] execute-inline/range
1011     case kFmt5rc:        // exop {vCCCC .. v(CCCC+AAAA-1)}, meth@BBBBBBBB
1012         {
1013             /*
1014              * This doesn't match the "dx" output when some of the args are
1015              * 64-bit values -- dx only shows the first register.
1016              */
1017             fputs(" {", stdout);
1018             for (i = 0; i < (int) pDecInsn->vA; i++) {
1019                 if (i == 0)
1020                     printf("v%d", pDecInsn->vC + i);
1021                 else
1022                     printf(", v%d", pDecInsn->vC + i);
1023             }
1024             printf("}, %s", indexBuf);
1025         }
1026         break;
1027     case kFmt51l:        // op vAA, #+BBBBBBBBBBBBBBBB
1028         {
1029             /* this is often, but not always, a double */
1030             union {
1031                 double d;
1032                 u8 j;
1033             } conv;
1034             conv.j = pDecInsn->vB_wide;
1035             printf(" v%d, #double %f // #%016llx",
1036                 pDecInsn->vA, conv.d, pDecInsn->vB_wide);
1037         }
1038         break;
1039     case kFmt00x:        // unknown op or breakpoint
1040         break;
1041     default:
1042         printf(" ???");
1043         break;
1044     }
1045 
1046     putchar('\n');
1047 
1048     if (indexBuf != indexBufChars) {
1049         free(indexBuf);
1050     }
1051 }
1052 
1053 /*
1054  * Dump a bytecode disassembly.
1055  */
dumpBytecodes(DexFile * pDexFile,const DexMethod * pDexMethod)1056 void dumpBytecodes(DexFile* pDexFile, const DexMethod* pDexMethod)
1057 {
1058     const DexCode* pCode = dexGetCode(pDexFile, pDexMethod);
1059     const u2* insns;
1060     int insnIdx;
1061     FieldMethodInfo methInfo;
1062     int startAddr;
1063     char* className = NULL;
1064 
1065     assert(pCode->insnsSize > 0);
1066     insns = pCode->insns;
1067 
1068     getMethodInfo(pDexFile, pDexMethod->methodIdx, &methInfo);
1069     startAddr = ((u1*)pCode - pDexFile->baseAddr);
1070     className = descriptorToDot(methInfo.classDescriptor);
1071 
1072     printf("%06x:                                        |[%06x] %s.%s:%s\n",
1073         startAddr, startAddr,
1074         className, methInfo.name, methInfo.signature);
1075 
1076     insnIdx = 0;
1077     while (insnIdx < (int) pCode->insnsSize) {
1078         int insnWidth;
1079         DecodedInstruction decInsn;
1080         u2 instr;
1081 
1082         /*
1083          * Note: This code parallels the function
1084          * dexGetWidthFromInstruction() in InstrUtils.c, but this version
1085          * can deal with data in either endianness.
1086          *
1087          * TODO: Figure out if this really matters, and possibly change
1088          * this to just use dexGetWidthFromInstruction().
1089          */
1090         instr = get2LE((const u1*)insns);
1091         if (instr == kPackedSwitchSignature) {
1092             insnWidth = 4 + get2LE((const u1*)(insns+1)) * 2;
1093         } else if (instr == kSparseSwitchSignature) {
1094             insnWidth = 2 + get2LE((const u1*)(insns+1)) * 4;
1095         } else if (instr == kArrayDataSignature) {
1096             int width = get2LE((const u1*)(insns+1));
1097             int size = get2LE((const u1*)(insns+2)) |
1098                        (get2LE((const u1*)(insns+3))<<16);
1099             // The plus 1 is to round up for odd size and width.
1100             insnWidth = 4 + ((size * width) + 1) / 2;
1101         } else {
1102             Opcode opcode = dexOpcodeFromCodeUnit(instr);
1103             insnWidth = dexGetWidthFromOpcode(opcode);
1104             if (insnWidth == 0) {
1105                 fprintf(stderr,
1106                     "GLITCH: zero-width instruction at idx=0x%04x\n", insnIdx);
1107                 break;
1108             }
1109         }
1110 
1111         dexDecodeInstruction(insns, &decInsn);
1112         dumpInstruction(pDexFile, pCode, insnIdx, insnWidth, &decInsn);
1113 
1114         insns += insnWidth;
1115         insnIdx += insnWidth;
1116     }
1117 
1118     free(className);
1119 }
1120 
1121 /*
1122  * Dump a "code" struct.
1123  */
dumpCode(DexFile * pDexFile,const DexMethod * pDexMethod)1124 void dumpCode(DexFile* pDexFile, const DexMethod* pDexMethod)
1125 {
1126     const DexCode* pCode = dexGetCode(pDexFile, pDexMethod);
1127 
1128     printf("      registers     : %d\n", pCode->registersSize);
1129     printf("      ins           : %d\n", pCode->insSize);
1130     printf("      outs          : %d\n", pCode->outsSize);
1131     printf("      insns size    : %d 16-bit code units\n", pCode->insnsSize);
1132 
1133     if (gOptions.disassemble)
1134         dumpBytecodes(pDexFile, pDexMethod);
1135 
1136     dumpCatches(pDexFile, pCode);
1137     /* both of these are encoded in debug info */
1138     dumpPositions(pDexFile, pCode, pDexMethod);
1139     dumpLocals(pDexFile, pCode, pDexMethod);
1140 }
1141 
1142 /*
1143  * Dump a method.
1144  */
dumpMethod(DexFile * pDexFile,const DexMethod * pDexMethod,int i)1145 void dumpMethod(DexFile* pDexFile, const DexMethod* pDexMethod, int i)
1146 {
1147     const DexMethodId* pMethodId;
1148     const char* backDescriptor;
1149     const char* name;
1150     char* typeDescriptor = NULL;
1151     char* accessStr = NULL;
1152 
1153     if (gOptions.exportsOnly &&
1154         (pDexMethod->accessFlags & (ACC_PUBLIC | ACC_PROTECTED)) == 0)
1155     {
1156         return;
1157     }
1158 
1159     pMethodId = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
1160     name = dexStringById(pDexFile, pMethodId->nameIdx);
1161     typeDescriptor = dexCopyDescriptorFromMethodId(pDexFile, pMethodId);
1162 
1163     backDescriptor = dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
1164 
1165     accessStr = createAccessFlagStr(pDexMethod->accessFlags,
1166                     kAccessForMethod);
1167 
1168     if (gOptions.outputFormat == OUTPUT_PLAIN) {
1169         printf("    #%d              : (in %s)\n", i, backDescriptor);
1170         printf("      name          : '%s'\n", name);
1171         printf("      type          : '%s'\n", typeDescriptor);
1172         printf("      access        : 0x%04x (%s)\n",
1173             pDexMethod->accessFlags, accessStr);
1174 
1175         if (pDexMethod->codeOff == 0) {
1176             printf("      code          : (none)\n");
1177         } else {
1178             printf("      code          -\n");
1179             dumpCode(pDexFile, pDexMethod);
1180         }
1181 
1182         if (gOptions.disassemble)
1183             putchar('\n');
1184     } else if (gOptions.outputFormat == OUTPUT_XML) {
1185         bool constructor = (name[0] == '<');
1186 
1187         if (constructor) {
1188             char* tmp;
1189 
1190             tmp = descriptorClassToDot(backDescriptor);
1191             printf("<constructor name=\"%s\"\n", tmp);
1192             free(tmp);
1193 
1194             tmp = descriptorToDot(backDescriptor);
1195             printf(" type=\"%s\"\n", tmp);
1196             free(tmp);
1197         } else {
1198             printf("<method name=\"%s\"\n", name);
1199 
1200             const char* returnType = strrchr(typeDescriptor, ')');
1201             if (returnType == NULL) {
1202                 fprintf(stderr, "bad method type descriptor '%s'\n",
1203                     typeDescriptor);
1204                 goto bail;
1205             }
1206 
1207             char* tmp = descriptorToDot(returnType+1);
1208             printf(" return=\"%s\"\n", tmp);
1209             free(tmp);
1210 
1211             printf(" abstract=%s\n",
1212                 quotedBool((pDexMethod->accessFlags & ACC_ABSTRACT) != 0));
1213             printf(" native=%s\n",
1214                 quotedBool((pDexMethod->accessFlags & ACC_NATIVE) != 0));
1215 
1216             bool isSync =
1217                 (pDexMethod->accessFlags & ACC_SYNCHRONIZED) != 0 ||
1218                 (pDexMethod->accessFlags & ACC_DECLARED_SYNCHRONIZED) != 0;
1219             printf(" synchronized=%s\n", quotedBool(isSync));
1220         }
1221 
1222         printf(" static=%s\n",
1223             quotedBool((pDexMethod->accessFlags & ACC_STATIC) != 0));
1224         printf(" final=%s\n",
1225             quotedBool((pDexMethod->accessFlags & ACC_FINAL) != 0));
1226         // "deprecated=" not knowable w/o parsing annotations
1227         printf(" visibility=%s\n",
1228             quotedVisibility(pDexMethod->accessFlags));
1229 
1230         printf(">\n");
1231 
1232         /*
1233          * Parameters.
1234          */
1235         if (typeDescriptor[0] != '(') {
1236             fprintf(stderr, "ERROR: bad descriptor '%s'\n", typeDescriptor);
1237             goto bail;
1238         }
1239 
1240         char tmpBuf[strlen(typeDescriptor)+1];      /* more than big enough */
1241         int argNum = 0;
1242 
1243         const char* base = typeDescriptor+1;
1244 
1245         while (*base != ')') {
1246             char* cp = tmpBuf;
1247 
1248             while (*base == '[')
1249                 *cp++ = *base++;
1250 
1251             if (*base == 'L') {
1252                 /* copy through ';' */
1253                 do {
1254                     *cp = *base++;
1255                 } while (*cp++ != ';');
1256             } else {
1257                 /* primitive char, copy it */
1258                 if (strchr("ZBCSIFJD", *base) == NULL) {
1259                     fprintf(stderr, "ERROR: bad method signature '%s'\n", base);
1260                     goto bail;
1261                 }
1262                 *cp++ = *base++;
1263             }
1264 
1265             /* null terminate and display */
1266             *cp++ = '\0';
1267 
1268             char* tmp = descriptorToDot(tmpBuf);
1269             printf("<parameter name=\"arg%d\" type=\"%s\">\n</parameter>\n",
1270                 argNum++, tmp);
1271             free(tmp);
1272         }
1273 
1274         if (constructor)
1275             printf("</constructor>\n");
1276         else
1277             printf("</method>\n");
1278     }
1279 
1280 bail:
1281     free(typeDescriptor);
1282     free(accessStr);
1283 }
1284 
1285 /*
1286  * Dump a static (class) field.
1287  */
dumpSField(const DexFile * pDexFile,const DexField * pSField,int i)1288 void dumpSField(const DexFile* pDexFile, const DexField* pSField, int i)
1289 {
1290     const DexFieldId* pFieldId;
1291     const char* backDescriptor;
1292     const char* name;
1293     const char* typeDescriptor;
1294     char* accessStr;
1295 
1296     if (gOptions.exportsOnly &&
1297         (pSField->accessFlags & (ACC_PUBLIC | ACC_PROTECTED)) == 0)
1298     {
1299         return;
1300     }
1301 
1302     pFieldId = dexGetFieldId(pDexFile, pSField->fieldIdx);
1303     name = dexStringById(pDexFile, pFieldId->nameIdx);
1304     typeDescriptor = dexStringByTypeIdx(pDexFile, pFieldId->typeIdx);
1305     backDescriptor = dexStringByTypeIdx(pDexFile, pFieldId->classIdx);
1306 
1307     accessStr = createAccessFlagStr(pSField->accessFlags, kAccessForField);
1308 
1309     if (gOptions.outputFormat == OUTPUT_PLAIN) {
1310         printf("    #%d              : (in %s)\n", i, backDescriptor);
1311         printf("      name          : '%s'\n", name);
1312         printf("      type          : '%s'\n", typeDescriptor);
1313         printf("      access        : 0x%04x (%s)\n",
1314             pSField->accessFlags, accessStr);
1315     } else if (gOptions.outputFormat == OUTPUT_XML) {
1316         char* tmp;
1317 
1318         printf("<field name=\"%s\"\n", name);
1319 
1320         tmp = descriptorToDot(typeDescriptor);
1321         printf(" type=\"%s\"\n", tmp);
1322         free(tmp);
1323 
1324         printf(" transient=%s\n",
1325             quotedBool((pSField->accessFlags & ACC_TRANSIENT) != 0));
1326         printf(" volatile=%s\n",
1327             quotedBool((pSField->accessFlags & ACC_VOLATILE) != 0));
1328         // "value=" not knowable w/o parsing annotations
1329         printf(" static=%s\n",
1330             quotedBool((pSField->accessFlags & ACC_STATIC) != 0));
1331         printf(" final=%s\n",
1332             quotedBool((pSField->accessFlags & ACC_FINAL) != 0));
1333         // "deprecated=" not knowable w/o parsing annotations
1334         printf(" visibility=%s\n",
1335             quotedVisibility(pSField->accessFlags));
1336         printf(">\n</field>\n");
1337     }
1338 
1339     free(accessStr);
1340 }
1341 
1342 /*
1343  * Dump an instance field.
1344  */
dumpIField(const DexFile * pDexFile,const DexField * pIField,int i)1345 void dumpIField(const DexFile* pDexFile, const DexField* pIField, int i)
1346 {
1347     dumpSField(pDexFile, pIField, i);
1348 }
1349 
1350 /*
1351  * Dump the class.
1352  *
1353  * Note "idx" is a DexClassDef index, not a DexTypeId index.
1354  *
1355  * If "*pLastPackage" is NULL or does not match the current class' package,
1356  * the value will be replaced with a newly-allocated string.
1357  */
dumpClass(DexFile * pDexFile,int idx,char ** pLastPackage)1358 void dumpClass(DexFile* pDexFile, int idx, char** pLastPackage)
1359 {
1360     const DexTypeList* pInterfaces;
1361     const DexClassDef* pClassDef;
1362     DexClassData* pClassData = NULL;
1363     const u1* pEncodedData;
1364     const char* fileName;
1365     const char* classDescriptor;
1366     const char* superclassDescriptor;
1367     char* accessStr = NULL;
1368     int i;
1369 
1370     pClassDef = dexGetClassDef(pDexFile, idx);
1371 
1372     if (gOptions.exportsOnly && (pClassDef->accessFlags & ACC_PUBLIC) == 0) {
1373         //printf("<!-- omitting non-public class %s -->\n",
1374         //    classDescriptor);
1375         goto bail;
1376     }
1377 
1378     pEncodedData = dexGetClassData(pDexFile, pClassDef);
1379     pClassData = dexReadAndVerifyClassData(&pEncodedData, NULL);
1380 
1381     if (pClassData == NULL) {
1382         printf("Trouble reading class data (#%d)\n", idx);
1383         goto bail;
1384     }
1385 
1386     classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
1387 
1388     /*
1389      * For the XML output, show the package name.  Ideally we'd gather
1390      * up the classes, sort them, and dump them alphabetically so the
1391      * package name wouldn't jump around, but that's not a great plan
1392      * for something that needs to run on the device.
1393      */
1394     if (!(classDescriptor[0] == 'L' &&
1395           classDescriptor[strlen(classDescriptor)-1] == ';'))
1396     {
1397         /* arrays and primitives should not be defined explicitly */
1398         fprintf(stderr, "Malformed class name '%s'\n", classDescriptor);
1399         /* keep going? */
1400     } else if (gOptions.outputFormat == OUTPUT_XML) {
1401         char* mangle;
1402         char* lastSlash;
1403         char* cp;
1404 
1405         mangle = strdup(classDescriptor + 1);
1406         mangle[strlen(mangle)-1] = '\0';
1407 
1408         /* reduce to just the package name */
1409         lastSlash = strrchr(mangle, '/');
1410         if (lastSlash != NULL) {
1411             *lastSlash = '\0';
1412         } else {
1413             *mangle = '\0';
1414         }
1415 
1416         for (cp = mangle; *cp != '\0'; cp++) {
1417             if (*cp == '/')
1418                 *cp = '.';
1419         }
1420 
1421         if (*pLastPackage == NULL || strcmp(mangle, *pLastPackage) != 0) {
1422             /* start of a new package */
1423             if (*pLastPackage != NULL)
1424                 printf("</package>\n");
1425             printf("<package name=\"%s\"\n>\n", mangle);
1426             free(*pLastPackage);
1427             *pLastPackage = mangle;
1428         } else {
1429             free(mangle);
1430         }
1431     }
1432 
1433     accessStr = createAccessFlagStr(pClassDef->accessFlags, kAccessForClass);
1434 
1435     if (pClassDef->superclassIdx == kDexNoIndex) {
1436         superclassDescriptor = NULL;
1437     } else {
1438         superclassDescriptor =
1439             dexStringByTypeIdx(pDexFile, pClassDef->superclassIdx);
1440     }
1441 
1442     if (gOptions.outputFormat == OUTPUT_PLAIN) {
1443         printf("Class #%d            -\n", idx);
1444         printf("  Class descriptor  : '%s'\n", classDescriptor);
1445         printf("  Access flags      : 0x%04x (%s)\n",
1446             pClassDef->accessFlags, accessStr);
1447 
1448         if (superclassDescriptor != NULL)
1449             printf("  Superclass        : '%s'\n", superclassDescriptor);
1450 
1451         printf("  Interfaces        -\n");
1452     } else {
1453         char* tmp;
1454 
1455         tmp = descriptorClassToDot(classDescriptor);
1456         printf("<class name=\"%s\"\n", tmp);
1457         free(tmp);
1458 
1459         if (superclassDescriptor != NULL) {
1460             tmp = descriptorToDot(superclassDescriptor);
1461             printf(" extends=\"%s\"\n", tmp);
1462             free(tmp);
1463         }
1464         printf(" abstract=%s\n",
1465             quotedBool((pClassDef->accessFlags & ACC_ABSTRACT) != 0));
1466         printf(" static=%s\n",
1467             quotedBool((pClassDef->accessFlags & ACC_STATIC) != 0));
1468         printf(" final=%s\n",
1469             quotedBool((pClassDef->accessFlags & ACC_FINAL) != 0));
1470         // "deprecated=" not knowable w/o parsing annotations
1471         printf(" visibility=%s\n",
1472             quotedVisibility(pClassDef->accessFlags));
1473         printf(">\n");
1474     }
1475     pInterfaces = dexGetInterfacesList(pDexFile, pClassDef);
1476     if (pInterfaces != NULL) {
1477         for (i = 0; i < (int) pInterfaces->size; i++)
1478             dumpInterface(pDexFile, dexGetTypeItem(pInterfaces, i), i);
1479     }
1480 
1481     if (gOptions.outputFormat == OUTPUT_PLAIN)
1482         printf("  Static fields     -\n");
1483     for (i = 0; i < (int) pClassData->header.staticFieldsSize; i++) {
1484         dumpSField(pDexFile, &pClassData->staticFields[i], i);
1485     }
1486 
1487     if (gOptions.outputFormat == OUTPUT_PLAIN)
1488         printf("  Instance fields   -\n");
1489     for (i = 0; i < (int) pClassData->header.instanceFieldsSize; i++) {
1490         dumpIField(pDexFile, &pClassData->instanceFields[i], i);
1491     }
1492 
1493     if (gOptions.outputFormat == OUTPUT_PLAIN)
1494         printf("  Direct methods    -\n");
1495     for (i = 0; i < (int) pClassData->header.directMethodsSize; i++) {
1496         dumpMethod(pDexFile, &pClassData->directMethods[i], i);
1497     }
1498 
1499     if (gOptions.outputFormat == OUTPUT_PLAIN)
1500         printf("  Virtual methods   -\n");
1501     for (i = 0; i < (int) pClassData->header.virtualMethodsSize; i++) {
1502         dumpMethod(pDexFile, &pClassData->virtualMethods[i], i);
1503     }
1504 
1505     // TODO: Annotations.
1506 
1507     if (pClassDef->sourceFileIdx != kDexNoIndex)
1508         fileName = dexStringById(pDexFile, pClassDef->sourceFileIdx);
1509     else
1510         fileName = "unknown";
1511 
1512     if (gOptions.outputFormat == OUTPUT_PLAIN) {
1513         printf("  source_file_idx   : %d (%s)\n",
1514             pClassDef->sourceFileIdx, fileName);
1515         printf("\n");
1516     }
1517 
1518     if (gOptions.outputFormat == OUTPUT_XML) {
1519         printf("</class>\n");
1520     }
1521 
1522 bail:
1523     free(pClassData);
1524     free(accessStr);
1525 }
1526 
1527 
1528 /*
1529  * Advance "ptr" to ensure 32-bit alignment.
1530  */
align32(const u1 * ptr)1531 static inline const u1* align32(const u1* ptr)
1532 {
1533     return (u1*) (((int) ptr + 3) & ~0x03);
1534 }
1535 
1536 
1537 /*
1538  * Dump a map in the "differential" format.
1539  *
1540  * TODO: show a hex dump of the compressed data.  (We can show the
1541  * uncompressed data if we move the compression code to libdex; otherwise
1542  * it's too complex to merit a fast & fragile implementation here.)
1543  */
dumpDifferentialCompressedMap(const u1 ** pData)1544 void dumpDifferentialCompressedMap(const u1** pData)
1545 {
1546     const u1* data = *pData;
1547     const u1* dataStart = data -1;      // format byte already removed
1548     u1 regWidth;
1549     u2 numEntries;
1550 
1551     /* standard header */
1552     regWidth = *data++;
1553     numEntries = *data++;
1554     numEntries |= (*data++) << 8;
1555 
1556     /* compressed data begins with the compressed data length */
1557     int compressedLen = readUnsignedLeb128(&data);
1558     int addrWidth = 1;
1559     if ((*data & 0x80) != 0)
1560         addrWidth++;
1561 
1562     int origLen = 4 + (addrWidth + regWidth) * numEntries;
1563     int compLen = (data - dataStart) + compressedLen;
1564 
1565     printf("        (differential compression %d -> %d [%d -> %d])\n",
1566         origLen, compLen,
1567         (addrWidth + regWidth) * numEntries, compressedLen);
1568 
1569     /* skip past end of entry */
1570     data += compressedLen;
1571 
1572     *pData = data;
1573 }
1574 
1575 /*
1576  * Dump register map contents of the current method.
1577  *
1578  * "*pData" should point to the start of the register map data.  Advances
1579  * "*pData" to the start of the next map.
1580  */
dumpMethodMap(DexFile * pDexFile,const DexMethod * pDexMethod,int idx,const u1 ** pData)1581 void dumpMethodMap(DexFile* pDexFile, const DexMethod* pDexMethod, int idx,
1582     const u1** pData)
1583 {
1584     const u1* data = *pData;
1585     const DexMethodId* pMethodId;
1586     const char* name;
1587     int offset = data - (u1*) pDexFile->pOptHeader;
1588 
1589     pMethodId = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
1590     name = dexStringById(pDexFile, pMethodId->nameIdx);
1591     printf("      #%d: 0x%08x %s\n", idx, offset, name);
1592 
1593     u1 format;
1594     int addrWidth;
1595 
1596     format = *data++;
1597     if (format == 1) {              /* kRegMapFormatNone */
1598         /* no map */
1599         printf("        (no map)\n");
1600         addrWidth = 0;
1601     } else if (format == 2) {       /* kRegMapFormatCompact8 */
1602         addrWidth = 1;
1603     } else if (format == 3) {       /* kRegMapFormatCompact16 */
1604         addrWidth = 2;
1605     } else if (format == 4) {       /* kRegMapFormatDifferential */
1606         dumpDifferentialCompressedMap(&data);
1607         goto bail;
1608     } else {
1609         printf("        (unknown format %d!)\n", format);
1610         /* don't know how to skip data; failure will cascade to end of class */
1611         goto bail;
1612     }
1613 
1614     if (addrWidth > 0) {
1615         u1 regWidth;
1616         u2 numEntries;
1617         int idx, addr, byte;
1618 
1619         regWidth = *data++;
1620         numEntries = *data++;
1621         numEntries |= (*data++) << 8;
1622 
1623         for (idx = 0; idx < numEntries; idx++) {
1624             addr = *data++;
1625             if (addrWidth > 1)
1626                 addr |= (*data++) << 8;
1627 
1628             printf("        %4x:", addr);
1629             for (byte = 0; byte < regWidth; byte++) {
1630                 printf(" %02x", *data++);
1631             }
1632             printf("\n");
1633         }
1634     }
1635 
1636 bail:
1637     //if (addrWidth >= 0)
1638     //    *pData = align32(data);
1639     *pData = data;
1640 }
1641 
1642 /*
1643  * Dump the contents of the register map area.
1644  *
1645  * These are only present in optimized DEX files, and the structure is
1646  * not really exposed to other parts of the VM itself.  We're going to
1647  * dig through them here, but this is pretty fragile.  DO NOT rely on
1648  * this or derive other code from it.
1649  */
dumpRegisterMaps(DexFile * pDexFile)1650 void dumpRegisterMaps(DexFile* pDexFile)
1651 {
1652     const u1* pClassPool = (const u1*)pDexFile->pRegisterMapPool;
1653     const u4* classOffsets;
1654     const u1* ptr;
1655     u4 numClasses;
1656     int baseFileOffset = (u1*) pClassPool - (u1*) pDexFile->pOptHeader;
1657     int idx;
1658 
1659     if (pClassPool == NULL) {
1660         printf("No register maps found\n");
1661         return;
1662     }
1663 
1664     ptr = pClassPool;
1665     numClasses = get4LE(ptr);
1666     ptr += sizeof(u4);
1667     classOffsets = (const u4*) ptr;
1668 
1669     printf("RMAP begins at offset 0x%07x\n", baseFileOffset);
1670     printf("Maps for %d classes\n", numClasses);
1671     for (idx = 0; idx < (int) numClasses; idx++) {
1672         const DexClassDef* pClassDef;
1673         const char* classDescriptor;
1674 
1675         pClassDef = dexGetClassDef(pDexFile, idx);
1676         classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
1677 
1678         printf("%4d: +%d (0x%08x) %s\n", idx, classOffsets[idx],
1679             baseFileOffset + classOffsets[idx], classDescriptor);
1680 
1681         if (classOffsets[idx] == 0)
1682             continue;
1683 
1684         /*
1685          * What follows is a series of RegisterMap entries, one for every
1686          * direct method, then one for every virtual method.
1687          */
1688         DexClassData* pClassData;
1689         const u1* pEncodedData;
1690         const u1* data = (u1*) pClassPool + classOffsets[idx];
1691         u2 methodCount;
1692         int i;
1693 
1694         pEncodedData = dexGetClassData(pDexFile, pClassDef);
1695         pClassData = dexReadAndVerifyClassData(&pEncodedData, NULL);
1696         if (pClassData == NULL) {
1697             fprintf(stderr, "Trouble reading class data\n");
1698             continue;
1699         }
1700 
1701         methodCount = *data++;
1702         methodCount |= (*data++) << 8;
1703         data += 2;      /* two pad bytes follow methodCount */
1704         if (methodCount != pClassData->header.directMethodsSize
1705                             + pClassData->header.virtualMethodsSize)
1706         {
1707             printf("NOTE: method count discrepancy (%d != %d + %d)\n",
1708                 methodCount, pClassData->header.directMethodsSize,
1709                 pClassData->header.virtualMethodsSize);
1710             /* this is bad, but keep going anyway */
1711         }
1712 
1713         printf("    direct methods: %d\n",
1714             pClassData->header.directMethodsSize);
1715         for (i = 0; i < (int) pClassData->header.directMethodsSize; i++) {
1716             dumpMethodMap(pDexFile, &pClassData->directMethods[i], i, &data);
1717         }
1718 
1719         printf("    virtual methods: %d\n",
1720             pClassData->header.virtualMethodsSize);
1721         for (i = 0; i < (int) pClassData->header.virtualMethodsSize; i++) {
1722             dumpMethodMap(pDexFile, &pClassData->virtualMethods[i], i, &data);
1723         }
1724 
1725         free(pClassData);
1726     }
1727 }
1728 
1729 /*
1730  * Dump the requested sections of the file.
1731  */
processDexFile(const char * fileName,DexFile * pDexFile)1732 void processDexFile(const char* fileName, DexFile* pDexFile)
1733 {
1734     char* package = NULL;
1735     int i;
1736 
1737     if (gOptions.verbose) {
1738         printf("Opened '%s', DEX version '%.3s'\n", fileName,
1739             pDexFile->pHeader->magic +4);
1740     }
1741 
1742     if (gOptions.dumpRegisterMaps) {
1743         dumpRegisterMaps(pDexFile);
1744         return;
1745     }
1746 
1747     if (gOptions.showFileHeaders) {
1748         dumpFileHeader(pDexFile);
1749         dumpOptDirectory(pDexFile);
1750     }
1751 
1752     if (gOptions.outputFormat == OUTPUT_XML)
1753         printf("<api>\n");
1754 
1755     for (i = 0; i < (int) pDexFile->pHeader->classDefsSize; i++) {
1756         if (gOptions.showSectionHeaders)
1757             dumpClassDef(pDexFile, i);
1758 
1759         dumpClass(pDexFile, i, &package);
1760     }
1761 
1762     /* free the last one allocated */
1763     if (package != NULL) {
1764         printf("</package>\n");
1765         free(package);
1766     }
1767 
1768     if (gOptions.outputFormat == OUTPUT_XML)
1769         printf("</api>\n");
1770 }
1771 
1772 
1773 /*
1774  * Process one file.
1775  */
process(const char * fileName)1776 int process(const char* fileName)
1777 {
1778     DexFile* pDexFile = NULL;
1779     MemMapping map;
1780     bool mapped = false;
1781     int result = -1;
1782 
1783     if (gOptions.verbose)
1784         printf("Processing '%s'...\n", fileName);
1785 
1786     if (dexOpenAndMap(fileName, gOptions.tempFileName, &map, false) != 0) {
1787         return result;
1788     }
1789     mapped = true;
1790 
1791     int flags = kDexParseVerifyChecksum;
1792     if (gOptions.ignoreBadChecksum)
1793         flags |= kDexParseContinueOnError;
1794 
1795     pDexFile = dexFileParse((u1*)map.addr, map.length, flags);
1796     if (pDexFile == NULL) {
1797         fprintf(stderr, "ERROR: DEX parse failed\n");
1798         goto bail;
1799     }
1800 
1801     if (gOptions.checksumOnly) {
1802         printf("Checksum verified\n");
1803     } else {
1804         processDexFile(fileName, pDexFile);
1805     }
1806 
1807     result = 0;
1808 
1809 bail:
1810     if (mapped)
1811         sysReleaseShmem(&map);
1812     if (pDexFile != NULL)
1813         dexFileFree(pDexFile);
1814     return result;
1815 }
1816 
1817 
1818 /*
1819  * Show usage.
1820  */
usage(void)1821 void usage(void)
1822 {
1823     fprintf(stderr, "Copyright (C) 2007 The Android Open Source Project\n\n");
1824     fprintf(stderr,
1825         "%s: [-c] [-d] [-f] [-h] [-i] [-l layout] [-m] [-t tempfile] dexfile...\n",
1826         gProgName);
1827     fprintf(stderr, "\n");
1828     fprintf(stderr, " -c : verify checksum and exit\n");
1829     fprintf(stderr, " -d : disassemble code sections\n");
1830     fprintf(stderr, " -f : display summary information from file header\n");
1831     fprintf(stderr, " -h : display file header details\n");
1832     fprintf(stderr, " -i : ignore checksum failures\n");
1833     fprintf(stderr, " -l : output layout, either 'plain' or 'xml'\n");
1834     fprintf(stderr, " -m : dump register maps (and nothing else)\n");
1835     fprintf(stderr, " -t : temp file name (defaults to /sdcard/dex-temp-*)\n");
1836 }
1837 
1838 /*
1839  * Parse args.
1840  *
1841  * I'm not using getopt_long() because we may not have it in libc.
1842  */
main(int argc,char * const argv[])1843 int main(int argc, char* const argv[])
1844 {
1845     bool wantUsage = false;
1846     int ic;
1847 
1848     memset(&gOptions, 0, sizeof(gOptions));
1849     gOptions.verbose = true;
1850 
1851     while (1) {
1852         ic = getopt(argc, argv, "cdfhil:mt:");
1853         if (ic < 0)
1854             break;
1855 
1856         switch (ic) {
1857         case 'c':       // verify the checksum then exit
1858             gOptions.checksumOnly = true;
1859             break;
1860         case 'd':       // disassemble Dalvik instructions
1861             gOptions.disassemble = true;
1862             break;
1863         case 'f':       // dump outer file header
1864             gOptions.showFileHeaders = true;
1865             break;
1866         case 'h':       // dump section headers, i.e. all meta-data
1867             gOptions.showSectionHeaders = true;
1868             break;
1869         case 'i':       // continue even if checksum is bad
1870             gOptions.ignoreBadChecksum = true;
1871             break;
1872         case 'l':       // layout
1873             if (strcmp(optarg, "plain") == 0) {
1874                 gOptions.outputFormat = OUTPUT_PLAIN;
1875             } else if (strcmp(optarg, "xml") == 0) {
1876                 gOptions.outputFormat = OUTPUT_XML;
1877                 gOptions.verbose = false;
1878                 gOptions.exportsOnly = true;
1879             } else {
1880                 wantUsage = true;
1881             }
1882             break;
1883         case 'm':       // dump register maps only
1884             gOptions.dumpRegisterMaps = true;
1885             break;
1886         case 't':       // temp file, used when opening compressed Jar
1887             gOptions.tempFileName = optarg;
1888             break;
1889         default:
1890             wantUsage = true;
1891             break;
1892         }
1893     }
1894 
1895     if (optind == argc) {
1896         fprintf(stderr, "%s: no file specified\n", gProgName);
1897         wantUsage = true;
1898     }
1899 
1900     if (gOptions.checksumOnly && gOptions.ignoreBadChecksum) {
1901         fprintf(stderr, "Can't specify both -c and -i\n");
1902         wantUsage = true;
1903     }
1904 
1905     if (wantUsage) {
1906         usage();
1907         return 2;
1908     }
1909 
1910     int result = 0;
1911     while (optind < argc) {
1912         result |= process(argv[optind++]);
1913     }
1914 
1915     return (result != 0);
1916 }
1917