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