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