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