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 /* basic info about a prototype */
86 struct ProtoInfo {
87 char* parameterTypes; // dynamically allocated with malloc
88 const char* returnType;
89 };
90
91 /*
92 * Get 2 little-endian bytes.
93 */
get2LE(unsigned char const * pSrc)94 static inline u2 get2LE(unsigned char const* pSrc)
95 {
96 return pSrc[0] | (pSrc[1] << 8);
97 }
98
99 /*
100 * Get 4 little-endian bytes.
101 */
get4LE(unsigned char const * pSrc)102 static inline u4 get4LE(unsigned char const* pSrc)
103 {
104 return pSrc[0] | (pSrc[1] << 8) | (pSrc[2] << 16) | (pSrc[3] << 24);
105 }
106
107 /*
108 * Converts a single-character primitive type into its human-readable
109 * equivalent.
110 */
primitiveTypeLabel(char typeChar)111 static const char* primitiveTypeLabel(char typeChar)
112 {
113 switch (typeChar) {
114 case 'B': return "byte";
115 case 'C': return "char";
116 case 'D': return "double";
117 case 'F': return "float";
118 case 'I': return "int";
119 case 'J': return "long";
120 case 'S': return "short";
121 case 'V': return "void";
122 case 'Z': return "boolean";
123 default:
124 return "UNKNOWN";
125 }
126 }
127
128 /*
129 * Converts a type descriptor to human-readable "dotted" form. For
130 * example, "Ljava/lang/String;" becomes "java.lang.String", and
131 * "[I" becomes "int[]".
132 */
descriptorToDot(const char * str)133 static char* descriptorToDot(const char* str)
134 {
135 int targetLen = strlen(str);
136 int offset = 0;
137 int arrayDepth = 0;
138 char* newStr;
139
140 /* strip leading [s; will be added to end */
141 while (targetLen > 1 && str[offset] == '[') {
142 offset++;
143 targetLen--;
144 }
145 arrayDepth = offset;
146
147 if (targetLen == 1) {
148 /* primitive type */
149 str = primitiveTypeLabel(str[offset]);
150 offset = 0;
151 targetLen = strlen(str);
152 } else {
153 /* account for leading 'L' and trailing ';' */
154 if (targetLen >= 2 && str[offset] == 'L' &&
155 str[offset+targetLen-1] == ';')
156 {
157 targetLen -= 2;
158 offset++;
159 }
160 }
161
162 newStr = (char*)malloc(targetLen + arrayDepth * 2 +1);
163
164 /* copy class name over */
165 int i;
166 for (i = 0; i < targetLen; i++) {
167 char ch = str[offset + i];
168 newStr[i] = (ch == '/') ? '.' : ch;
169 }
170
171 /* add the appropriate number of brackets for arrays */
172 while (arrayDepth-- > 0) {
173 newStr[i++] = '[';
174 newStr[i++] = ']';
175 }
176 newStr[i] = '\0';
177 assert(i == targetLen + arrayDepth * 2);
178
179 return newStr;
180 }
181
182 /*
183 * Retrieves the class name portion of a type descriptor.
184 *
185 * Returns a newly-allocated string.
186 */
descriptorClassToName(const char * str)187 static char* descriptorClassToName(const char* str)
188 {
189 const char* lastSlash;
190 char* newStr;
191
192 /* reduce to just the class name, trimming trailing ';' */
193 lastSlash = strrchr(str, '/');
194 if (lastSlash == NULL)
195 lastSlash = str + 1; /* start past 'L' */
196 else
197 lastSlash++; /* start past '/' */
198
199 newStr = strdup(lastSlash);
200 newStr[strlen(lastSlash)-1] = '\0';
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 * Get information about a ProtoId.
696 */
getProtoInfo(DexFile * pDexFile,u4 protoIdx,ProtoInfo * pProtoInfo)697 bool getProtoInfo(DexFile* pDexFile, u4 protoIdx, ProtoInfo* pProtoInfo)
698 {
699 if (protoIdx >= pDexFile->pHeader->protoIdsSize) {
700 return false;
701 }
702
703 const DexProtoId* protoId = dexGetProtoId(pDexFile, protoIdx);
704
705 // Get string for return type.
706 if (protoId->returnTypeIdx >= pDexFile->pHeader->typeIdsSize) {
707 return false;
708 }
709 pProtoInfo->returnType = dexStringByTypeIdx(pDexFile, protoId->returnTypeIdx);
710
711 // Build string for parameter types.
712 size_t bufSize = 1;
713 char* buf = (char*)malloc(bufSize);
714 if (buf == NULL) {
715 return false;
716 }
717
718 buf[0] = '\0';
719 size_t bufUsed = 1;
720
721 const DexTypeList* paramTypes = dexGetProtoParameters(pDexFile, protoId);
722 if (paramTypes == NULL) {
723 // No parameters.
724 pProtoInfo->parameterTypes = buf;
725 return true;
726 }
727
728 for (u4 i = 0; i < paramTypes->size; ++i) {
729 if (paramTypes->list[i].typeIdx >= pDexFile->pHeader->typeIdsSize) {
730 free(buf);
731 return false;
732 }
733 const char* param = dexStringByTypeIdx(pDexFile, paramTypes->list[i].typeIdx);
734 size_t paramLen = strlen(param);
735 size_t newUsed = bufUsed + paramLen;
736 if (newUsed > bufSize) {
737 char* newBuf = (char*)realloc(buf, newUsed);
738 if (newBuf == NULL) {
739 free(buf);
740 return false;
741 }
742 buf = newBuf;
743 bufSize = newUsed;
744 }
745 memcpy(buf + bufUsed - 1, param, paramLen + 1);
746 bufUsed = newUsed;
747 }
748
749 pProtoInfo->parameterTypes = buf;
750 return true;
751 }
752
753 /*
754 * Look up a class' descriptor.
755 */
getClassDescriptor(DexFile * pDexFile,u4 classIdx)756 const char* getClassDescriptor(DexFile* pDexFile, u4 classIdx)
757 {
758 return dexStringByTypeIdx(pDexFile, classIdx);
759 }
760
761 /*
762 * Helper for dumpInstruction(), which builds the string
763 * representation for the index in the given instruction. This will
764 * first try to use the given buffer, but if the result won't fit,
765 * then this will allocate a new buffer to hold the result. A pointer
766 * to the buffer which holds the full result is always returned, and
767 * this can be compared with the one passed in, to see if the result
768 * needs to be free()d.
769 */
indexString(DexFile * pDexFile,const DecodedInstruction * pDecInsn,size_t bufSize)770 static char* indexString(DexFile* pDexFile, const DecodedInstruction* pDecInsn, size_t bufSize)
771 {
772 char* buf = (char*)malloc(bufSize);
773 if (buf == NULL) {
774 return NULL;
775 }
776
777 int outSize;
778 u4 index;
779 u4 secondaryIndex = 0;
780 u4 width;
781
782 /* TODO: Make the index *always* be in field B, to simplify this code. */
783 switch (dexGetFormatFromOpcode(pDecInsn->opcode)) {
784 case kFmt20bc:
785 case kFmt21c:
786 case kFmt35c:
787 case kFmt35ms:
788 case kFmt3rc:
789 case kFmt3rms:
790 case kFmt35mi:
791 case kFmt3rmi:
792 index = pDecInsn->vB;
793 width = 4;
794 break;
795 case kFmt31c:
796 index = pDecInsn->vB;
797 width = 8;
798 break;
799 case kFmt22c:
800 case kFmt22cs:
801 index = pDecInsn->vC;
802 width = 4;
803 break;
804 case kFmt45cc:
805 case kFmt4rcc:
806 index = pDecInsn->vB; // method index
807 secondaryIndex = pDecInsn->arg[4]; // proto index
808 width = 4;
809 break;
810 default:
811 index = 0;
812 width = 4;
813 break;
814 }
815
816 switch (pDecInsn->indexType) {
817 case kIndexUnknown:
818 /*
819 * This function shouldn't ever get called for this type, but do
820 * something sensible here, just to help with debugging.
821 */
822 outSize = snprintf(buf, bufSize, "<unknown-index>");
823 break;
824 case kIndexNone:
825 /*
826 * This function shouldn't ever get called for this type, but do
827 * something sensible here, just to help with debugging.
828 */
829 outSize = snprintf(buf, bufSize, "<no-index>");
830 break;
831 case kIndexVaries:
832 /*
833 * This one should never show up in a dexdump, so no need to try
834 * to get fancy here.
835 */
836 outSize = snprintf(buf, bufSize, "<index-varies> // thing@%0*x",
837 width, index);
838 break;
839 case kIndexTypeRef:
840 if (index < pDexFile->pHeader->typeIdsSize) {
841 outSize = snprintf(buf, bufSize, "%s // type@%0*x",
842 getClassDescriptor(pDexFile, index), width, index);
843 } else {
844 outSize = snprintf(buf, bufSize, "<type?> // type@%0*x", width, index);
845 }
846 break;
847 case kIndexStringRef:
848 if (index < pDexFile->pHeader->stringIdsSize) {
849 outSize = snprintf(buf, bufSize, "\"%s\" // string@%0*x",
850 dexStringById(pDexFile, index), width, index);
851 } else {
852 outSize = snprintf(buf, bufSize, "<string?> // string@%0*x",
853 width, index);
854 }
855 break;
856 case kIndexMethodRef:
857 {
858 FieldMethodInfo methInfo;
859 if (getMethodInfo(pDexFile, index, &methInfo)) {
860 outSize = snprintf(buf, bufSize, "%s.%s:%s // method@%0*x",
861 methInfo.classDescriptor, methInfo.name,
862 methInfo.signature, width, index);
863 free((void *) methInfo.signature);
864 } else {
865 outSize = snprintf(buf, bufSize, "<method?> // method@%0*x",
866 width, index);
867 }
868 }
869 break;
870 case kIndexFieldRef:
871 {
872 FieldMethodInfo fieldInfo;
873 if (getFieldInfo(pDexFile, index, &fieldInfo)) {
874 outSize = snprintf(buf, bufSize, "%s.%s:%s // field@%0*x",
875 fieldInfo.classDescriptor, fieldInfo.name,
876 fieldInfo.signature, width, index);
877 } else {
878 outSize = snprintf(buf, bufSize, "<field?> // field@%0*x",
879 width, index);
880 }
881 }
882 break;
883 case kIndexInlineMethod:
884 outSize = snprintf(buf, bufSize, "[%0*x] // inline #%0*x",
885 width, index, width, index);
886 break;
887 case kIndexVtableOffset:
888 outSize = snprintf(buf, bufSize, "[%0*x] // vtable #%0*x",
889 width, index, width, index);
890 break;
891 case kIndexFieldOffset:
892 outSize = snprintf(buf, bufSize, "[obj+%0*x]", width, index);
893 break;
894 case kIndexMethodAndProtoRef:
895 {
896 FieldMethodInfo methInfo;
897 ProtoInfo protoInfo;
898 protoInfo.parameterTypes = NULL;
899 if (getMethodInfo(pDexFile, index, &methInfo) &&
900 getProtoInfo(pDexFile, secondaryIndex, &protoInfo)) {
901 outSize = snprintf(buf, bufSize, "%s.%s:%s, (%s)%s // method@%0*x, proto@%0*x",
902 methInfo.classDescriptor, methInfo.name, methInfo.signature,
903 protoInfo.parameterTypes, protoInfo.returnType,
904 width, index, width, secondaryIndex);
905 } else {
906 outSize = snprintf(buf, bufSize, "<method?>, <proto?> // method@%0*x, proto@%0*x",
907 width, index, width, secondaryIndex);
908 }
909 free(protoInfo.parameterTypes);
910 }
911 break;
912 case kIndexCallSiteRef:
913 outSize = snprintf(buf, bufSize, "call_site@%0*x", width, index);
914 break;
915 case kIndexMethodHandleRef:
916 outSize = snprintf(buf, bufSize, "methodhandle@%0*x", width, index);
917 break;
918 case kIndexProtoRef:
919 {
920 ProtoInfo protoInfo;
921 if (getProtoInfo(pDexFile, index, &protoInfo)) {
922 outSize = snprintf(buf, bufSize, "(%s)%s // proto@%0*x",
923 protoInfo.parameterTypes, protoInfo.returnType,
924 width, index);
925
926 } else {
927 outSize = snprintf(buf, bufSize, "<proto?> // proto@%0*x",
928 width, secondaryIndex);
929 }
930 free(protoInfo.parameterTypes);
931 }
932 break;
933 default:
934 outSize = snprintf(buf, bufSize, "<?>");
935 break;
936 }
937
938 if (outSize >= (int) bufSize) {
939 /*
940 * The buffer wasn't big enough; allocate and retry. Note:
941 * snprintf() doesn't count the '\0' as part of its returned
942 * size, so we add explicit space for it here.
943 */
944 free(buf);
945 return indexString(pDexFile, pDecInsn, outSize + 1);
946 } else {
947 return buf;
948 }
949 }
950
951 /*
952 * Dump a single instruction.
953 */
dumpInstruction(DexFile * pDexFile,const DexCode * pCode,int insnIdx,int insnWidth,const DecodedInstruction * pDecInsn)954 void dumpInstruction(DexFile* pDexFile, const DexCode* pCode, int insnIdx,
955 int insnWidth, const DecodedInstruction* pDecInsn)
956 {
957 const u2* insns = pCode->insns;
958 int i;
959
960 // Address of instruction (expressed as byte offset).
961 printf("%06zx:", ((u1*)insns - pDexFile->baseAddr) + insnIdx*2);
962
963 for (i = 0; i < 8; i++) {
964 if (i < insnWidth) {
965 if (i == 7) {
966 printf(" ... ");
967 } else {
968 /* print 16-bit value in little-endian order */
969 const u1* bytePtr = (const u1*) &insns[insnIdx+i];
970 printf(" %02x%02x", bytePtr[0], bytePtr[1]);
971 }
972 } else {
973 fputs(" ", stdout);
974 }
975 }
976
977 if (pDecInsn->opcode == OP_NOP) {
978 u2 instr = get2LE((const u1*) &insns[insnIdx]);
979 if (instr == kPackedSwitchSignature) {
980 printf("|%04x: packed-switch-data (%d units)",
981 insnIdx, insnWidth);
982 } else if (instr == kSparseSwitchSignature) {
983 printf("|%04x: sparse-switch-data (%d units)",
984 insnIdx, insnWidth);
985 } else if (instr == kArrayDataSignature) {
986 printf("|%04x: array-data (%d units)",
987 insnIdx, insnWidth);
988 } else {
989 printf("|%04x: nop // spacer", insnIdx);
990 }
991 } else {
992 printf("|%04x: %s", insnIdx, dexGetOpcodeName(pDecInsn->opcode));
993 }
994
995 // Provide an initial buffer that usually suffices, although indexString()
996 // may reallocate the buffer if more space is needed.
997 char* indexBuf = NULL;
998 if (pDecInsn->indexType != kIndexNone) {
999 indexBuf = indexString(pDexFile, pDecInsn, 200);
1000 }
1001
1002 switch (dexGetFormatFromOpcode(pDecInsn->opcode)) {
1003 case kFmt10x: // op
1004 break;
1005 case kFmt12x: // op vA, vB
1006 printf(" v%d, v%d", pDecInsn->vA, pDecInsn->vB);
1007 break;
1008 case kFmt11n: // op vA, #+B
1009 printf(" v%d, #int %d // #%x",
1010 pDecInsn->vA, (s4)pDecInsn->vB, (u1)pDecInsn->vB);
1011 break;
1012 case kFmt11x: // op vAA
1013 printf(" v%d", pDecInsn->vA);
1014 break;
1015 case kFmt10t: // op +AA
1016 case kFmt20t: // op +AAAA
1017 {
1018 s4 targ = (s4) pDecInsn->vA;
1019 printf(" %04x // %c%04x",
1020 insnIdx + targ,
1021 (targ < 0) ? '-' : '+',
1022 (targ < 0) ? -targ : targ);
1023 }
1024 break;
1025 case kFmt22x: // op vAA, vBBBB
1026 printf(" v%d, v%d", pDecInsn->vA, pDecInsn->vB);
1027 break;
1028 case kFmt21t: // op vAA, +BBBB
1029 {
1030 s4 targ = (s4) pDecInsn->vB;
1031 printf(" v%d, %04x // %c%04x", pDecInsn->vA,
1032 insnIdx + targ,
1033 (targ < 0) ? '-' : '+',
1034 (targ < 0) ? -targ : targ);
1035 }
1036 break;
1037 case kFmt21s: // op vAA, #+BBBB
1038 printf(" v%d, #int %d // #%x",
1039 pDecInsn->vA, (s4)pDecInsn->vB, (u2)pDecInsn->vB);
1040 break;
1041 case kFmt21h: // op vAA, #+BBBB0000[00000000]
1042 // The printed format varies a bit based on the actual opcode.
1043 if (pDecInsn->opcode == OP_CONST_HIGH16) {
1044 s4 value = pDecInsn->vB << 16;
1045 printf(" v%d, #int %d // #%x",
1046 pDecInsn->vA, value, (u2)pDecInsn->vB);
1047 } else {
1048 s8 value = ((s8) pDecInsn->vB) << 48;
1049 printf(" v%d, #long %" PRId64 " // #%x",
1050 pDecInsn->vA, value, (u2)pDecInsn->vB);
1051 }
1052 break;
1053 case kFmt21c: // op vAA, thing@BBBB
1054 case kFmt31c: // op vAA, thing@BBBBBBBB
1055 printf(" v%d, %s", pDecInsn->vA, indexBuf);
1056 break;
1057 case kFmt23x: // op vAA, vBB, vCC
1058 printf(" v%d, v%d, v%d", pDecInsn->vA, pDecInsn->vB, pDecInsn->vC);
1059 break;
1060 case kFmt22b: // op vAA, vBB, #+CC
1061 printf(" v%d, v%d, #int %d // #%02x",
1062 pDecInsn->vA, pDecInsn->vB, (s4)pDecInsn->vC, (u1)pDecInsn->vC);
1063 break;
1064 case kFmt22t: // op vA, vB, +CCCC
1065 {
1066 s4 targ = (s4) pDecInsn->vC;
1067 printf(" v%d, v%d, %04x // %c%04x", pDecInsn->vA, pDecInsn->vB,
1068 insnIdx + targ,
1069 (targ < 0) ? '-' : '+',
1070 (targ < 0) ? -targ : targ);
1071 }
1072 break;
1073 case kFmt22s: // op vA, vB, #+CCCC
1074 printf(" v%d, v%d, #int %d // #%04x",
1075 pDecInsn->vA, pDecInsn->vB, (s4)pDecInsn->vC, (u2)pDecInsn->vC);
1076 break;
1077 case kFmt22c: // op vA, vB, thing@CCCC
1078 case kFmt22cs: // [opt] op vA, vB, field offset CCCC
1079 printf(" v%d, v%d, %s", pDecInsn->vA, pDecInsn->vB, indexBuf);
1080 break;
1081 case kFmt30t:
1082 printf(" #%08x", pDecInsn->vA);
1083 break;
1084 case kFmt31i: // op vAA, #+BBBBBBBB
1085 {
1086 /* this is often, but not always, a float */
1087 union {
1088 float f;
1089 u4 i;
1090 } conv;
1091 conv.i = pDecInsn->vB;
1092 printf(" v%d, #float %f // #%08x",
1093 pDecInsn->vA, conv.f, pDecInsn->vB);
1094 }
1095 break;
1096 case kFmt31t: // op vAA, offset +BBBBBBBB
1097 printf(" v%d, %08x // +%08x",
1098 pDecInsn->vA, insnIdx + pDecInsn->vB, pDecInsn->vB);
1099 break;
1100 case kFmt32x: // op vAAAA, vBBBB
1101 printf(" v%d, v%d", pDecInsn->vA, pDecInsn->vB);
1102 break;
1103 case kFmt35c: // op {vC, vD, vE, vF, vG}, thing@BBBB
1104 case kFmt35ms: // [opt] invoke-virtual+super
1105 case kFmt35mi: // [opt] inline invoke
1106 {
1107 fputs(" {", stdout);
1108 for (i = 0; i < (int) pDecInsn->vA; i++) {
1109 if (i == 0)
1110 printf("v%d", pDecInsn->arg[i]);
1111 else
1112 printf(", v%d", pDecInsn->arg[i]);
1113 }
1114 printf("}, %s", indexBuf);
1115 }
1116 break;
1117 case kFmt3rc: // op {vCCCC .. v(CCCC+AA-1)}, thing@BBBB
1118 case kFmt3rms: // [opt] invoke-virtual+super/range
1119 case kFmt3rmi: // [opt] execute-inline/range
1120 {
1121 /*
1122 * This doesn't match the "dx" output when some of the args are
1123 * 64-bit values -- dx only shows the first register.
1124 */
1125 fputs(" {", stdout);
1126 for (i = 0; i < (int) pDecInsn->vA; i++) {
1127 if (i == 0)
1128 printf("v%d", pDecInsn->vC + i);
1129 else
1130 printf(", v%d", pDecInsn->vC + i);
1131 }
1132 printf("}, %s", indexBuf);
1133 }
1134 break;
1135 case kFmt51l: // op vAA, #+BBBBBBBBBBBBBBBB
1136 {
1137 /* this is often, but not always, a double */
1138 union {
1139 double d;
1140 u8 j;
1141 } conv;
1142 conv.j = pDecInsn->vB_wide;
1143 printf(" v%d, #double %f // #%016" PRIx64,
1144 pDecInsn->vA, conv.d, pDecInsn->vB_wide);
1145 }
1146 break;
1147 case kFmt00x: // unknown op or breakpoint
1148 break;
1149 case kFmt45cc:
1150 {
1151 fputs(" {", stdout);
1152 printf("v%d", pDecInsn->vC);
1153 for (int i = 0; i < (int) pDecInsn->vA - 1; ++i) {
1154 printf(", v%d", pDecInsn->arg[i]);
1155 }
1156 printf("}, %s", indexBuf);
1157 }
1158 break;
1159 case kFmt4rcc:
1160 {
1161 fputs(" {", stdout);
1162 printf("v%d", pDecInsn->vC);
1163 for (int i = 1; i < (int) pDecInsn->vA; ++i) {
1164 printf(", v%d", pDecInsn->vC + i);
1165 }
1166 printf("}, %s", indexBuf);
1167 }
1168 break;
1169 default:
1170 printf(" ???");
1171 break;
1172 }
1173
1174 putchar('\n');
1175
1176 free(indexBuf);
1177 }
1178
1179 /*
1180 * Dump a bytecode disassembly.
1181 */
dumpBytecodes(DexFile * pDexFile,const DexMethod * pDexMethod)1182 void dumpBytecodes(DexFile* pDexFile, const DexMethod* pDexMethod)
1183 {
1184 const DexCode* pCode = dexGetCode(pDexFile, pDexMethod);
1185 const u2* insns;
1186 int insnIdx;
1187 FieldMethodInfo methInfo;
1188 int startAddr;
1189 char* className = NULL;
1190
1191 assert(pCode->insnsSize > 0);
1192 insns = pCode->insns;
1193
1194 methInfo.classDescriptor =
1195 methInfo.name =
1196 methInfo.signature = NULL;
1197
1198 getMethodInfo(pDexFile, pDexMethod->methodIdx, &methInfo);
1199 startAddr = ((u1*)pCode - pDexFile->baseAddr);
1200 className = descriptorToDot(methInfo.classDescriptor);
1201
1202 printf("%06x: |[%06x] %s.%s:%s\n",
1203 startAddr, startAddr,
1204 className, methInfo.name, methInfo.signature);
1205 free((void *) methInfo.signature);
1206
1207 insnIdx = 0;
1208 while (insnIdx < (int) pCode->insnsSize) {
1209 int insnWidth;
1210 DecodedInstruction decInsn;
1211 u2 instr;
1212
1213 /*
1214 * Note: This code parallels the function
1215 * dexGetWidthFromInstruction() in InstrUtils.c, but this version
1216 * can deal with data in either endianness.
1217 *
1218 * TODO: Figure out if this really matters, and possibly change
1219 * this to just use dexGetWidthFromInstruction().
1220 */
1221 instr = get2LE((const u1*)insns);
1222 if (instr == kPackedSwitchSignature) {
1223 insnWidth = 4 + get2LE((const u1*)(insns+1)) * 2;
1224 } else if (instr == kSparseSwitchSignature) {
1225 insnWidth = 2 + get2LE((const u1*)(insns+1)) * 4;
1226 } else if (instr == kArrayDataSignature) {
1227 int width = get2LE((const u1*)(insns+1));
1228 int size = get2LE((const u1*)(insns+2)) |
1229 (get2LE((const u1*)(insns+3))<<16);
1230 // The plus 1 is to round up for odd size and width.
1231 insnWidth = 4 + ((size * width) + 1) / 2;
1232 } else {
1233 Opcode opcode = dexOpcodeFromCodeUnit(instr);
1234 insnWidth = dexGetWidthFromOpcode(opcode);
1235 if (insnWidth == 0) {
1236 fprintf(stderr,
1237 "GLITCH: zero-width instruction at idx=0x%04x\n", insnIdx);
1238 break;
1239 }
1240 }
1241
1242 dexDecodeInstruction(insns, &decInsn);
1243 dumpInstruction(pDexFile, pCode, insnIdx, insnWidth, &decInsn);
1244
1245 insns += insnWidth;
1246 insnIdx += insnWidth;
1247 }
1248
1249 free(className);
1250 }
1251
1252 /*
1253 * Dump a "code" struct.
1254 */
dumpCode(DexFile * pDexFile,const DexMethod * pDexMethod)1255 void dumpCode(DexFile* pDexFile, const DexMethod* pDexMethod)
1256 {
1257 const DexCode* pCode = dexGetCode(pDexFile, pDexMethod);
1258
1259 printf(" registers : %d\n", pCode->registersSize);
1260 printf(" ins : %d\n", pCode->insSize);
1261 printf(" outs : %d\n", pCode->outsSize);
1262 printf(" insns size : %d 16-bit code units\n", pCode->insnsSize);
1263
1264 if (gOptions.disassemble)
1265 dumpBytecodes(pDexFile, pDexMethod);
1266
1267 dumpCatches(pDexFile, pCode);
1268 /* both of these are encoded in debug info */
1269 dumpPositions(pDexFile, pCode, pDexMethod);
1270 dumpLocals(pDexFile, pCode, pDexMethod);
1271 }
1272
1273 /*
1274 * Dump a method.
1275 */
dumpMethod(DexFile * pDexFile,const DexMethod * pDexMethod,int i)1276 void dumpMethod(DexFile* pDexFile, const DexMethod* pDexMethod, int i)
1277 {
1278 const DexMethodId* pMethodId;
1279 const char* backDescriptor;
1280 const char* name;
1281 char* typeDescriptor = NULL;
1282 char* accessStr = NULL;
1283
1284 if (gOptions.exportsOnly &&
1285 (pDexMethod->accessFlags & (ACC_PUBLIC | ACC_PROTECTED)) == 0)
1286 {
1287 return;
1288 }
1289
1290 pMethodId = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
1291 name = dexStringById(pDexFile, pMethodId->nameIdx);
1292 typeDescriptor = dexCopyDescriptorFromMethodId(pDexFile, pMethodId);
1293
1294 backDescriptor = dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
1295
1296 accessStr = createAccessFlagStr(pDexMethod->accessFlags,
1297 kAccessForMethod);
1298
1299 if (gOptions.outputFormat == OUTPUT_PLAIN) {
1300 printf(" #%d : (in %s)\n", i, backDescriptor);
1301 printf(" name : '%s'\n", name);
1302 printf(" type : '%s'\n", typeDescriptor);
1303 printf(" access : 0x%04x (%s)\n",
1304 pDexMethod->accessFlags, accessStr);
1305
1306 if (pDexMethod->codeOff == 0) {
1307 printf(" code : (none)\n");
1308 } else {
1309 printf(" code -\n");
1310 dumpCode(pDexFile, pDexMethod);
1311 }
1312
1313 if (gOptions.disassemble)
1314 putchar('\n');
1315 } else if (gOptions.outputFormat == OUTPUT_XML) {
1316 bool constructor = (name[0] == '<');
1317
1318 if (constructor) {
1319 char* tmp;
1320
1321 tmp = descriptorClassToName(backDescriptor);
1322 printf("<constructor name=\"%s\"\n", tmp);
1323 free(tmp);
1324
1325 tmp = descriptorToDot(backDescriptor);
1326 printf(" type=\"%s\"\n", tmp);
1327 free(tmp);
1328 } else {
1329 printf("<method name=\"%s\"\n", name);
1330
1331 const char* returnType = strrchr(typeDescriptor, ')');
1332 if (returnType == NULL) {
1333 fprintf(stderr, "bad method type descriptor '%s'\n",
1334 typeDescriptor);
1335 goto bail;
1336 }
1337
1338 char* tmp = descriptorToDot(returnType+1);
1339 printf(" return=\"%s\"\n", tmp);
1340 free(tmp);
1341
1342 printf(" abstract=%s\n",
1343 quotedBool((pDexMethod->accessFlags & ACC_ABSTRACT) != 0));
1344 printf(" native=%s\n",
1345 quotedBool((pDexMethod->accessFlags & ACC_NATIVE) != 0));
1346
1347 bool isSync =
1348 (pDexMethod->accessFlags & ACC_SYNCHRONIZED) != 0 ||
1349 (pDexMethod->accessFlags & ACC_DECLARED_SYNCHRONIZED) != 0;
1350 printf(" synchronized=%s\n", quotedBool(isSync));
1351 }
1352
1353 printf(" static=%s\n",
1354 quotedBool((pDexMethod->accessFlags & ACC_STATIC) != 0));
1355 printf(" final=%s\n",
1356 quotedBool((pDexMethod->accessFlags & ACC_FINAL) != 0));
1357 // "deprecated=" not knowable w/o parsing annotations
1358 printf(" visibility=%s\n",
1359 quotedVisibility(pDexMethod->accessFlags));
1360
1361 printf(">\n");
1362
1363 /*
1364 * Parameters.
1365 */
1366 if (typeDescriptor[0] != '(') {
1367 fprintf(stderr, "ERROR: bad descriptor '%s'\n", typeDescriptor);
1368 goto bail;
1369 }
1370
1371 char tmpBuf[strlen(typeDescriptor)+1]; /* more than big enough */
1372 int argNum = 0;
1373
1374 const char* base = typeDescriptor+1;
1375
1376 while (*base != ')') {
1377 char* cp = tmpBuf;
1378
1379 while (*base == '[')
1380 *cp++ = *base++;
1381
1382 if (*base == 'L') {
1383 /* copy through ';' */
1384 do {
1385 *cp = *base++;
1386 } while (*cp++ != ';');
1387 } else {
1388 /* primitive char, copy it */
1389 if (strchr("ZBCSIFJD", *base) == NULL) {
1390 fprintf(stderr, "ERROR: bad method signature '%s'\n", base);
1391 goto bail;
1392 }
1393 *cp++ = *base++;
1394 }
1395
1396 /* null terminate and display */
1397 *cp++ = '\0';
1398
1399 char* tmp = descriptorToDot(tmpBuf);
1400 printf("<parameter name=\"arg%d\" type=\"%s\">\n</parameter>\n",
1401 argNum++, tmp);
1402 free(tmp);
1403 }
1404
1405 if (constructor)
1406 printf("</constructor>\n");
1407 else
1408 printf("</method>\n");
1409 }
1410
1411 bail:
1412 free(typeDescriptor);
1413 free(accessStr);
1414 }
1415
1416 /*
1417 * Dump a static (class) field.
1418 */
dumpSField(const DexFile * pDexFile,const DexField * pSField,int i)1419 void dumpSField(const DexFile* pDexFile, const DexField* pSField, int i)
1420 {
1421 const DexFieldId* pFieldId;
1422 const char* backDescriptor;
1423 const char* name;
1424 const char* typeDescriptor;
1425 char* accessStr;
1426
1427 if (gOptions.exportsOnly &&
1428 (pSField->accessFlags & (ACC_PUBLIC | ACC_PROTECTED)) == 0)
1429 {
1430 return;
1431 }
1432
1433 pFieldId = dexGetFieldId(pDexFile, pSField->fieldIdx);
1434 name = dexStringById(pDexFile, pFieldId->nameIdx);
1435 typeDescriptor = dexStringByTypeIdx(pDexFile, pFieldId->typeIdx);
1436 backDescriptor = dexStringByTypeIdx(pDexFile, pFieldId->classIdx);
1437
1438 accessStr = createAccessFlagStr(pSField->accessFlags, kAccessForField);
1439
1440 if (gOptions.outputFormat == OUTPUT_PLAIN) {
1441 printf(" #%d : (in %s)\n", i, backDescriptor);
1442 printf(" name : '%s'\n", name);
1443 printf(" type : '%s'\n", typeDescriptor);
1444 printf(" access : 0x%04x (%s)\n",
1445 pSField->accessFlags, accessStr);
1446 } else if (gOptions.outputFormat == OUTPUT_XML) {
1447 char* tmp;
1448
1449 printf("<field name=\"%s\"\n", name);
1450
1451 tmp = descriptorToDot(typeDescriptor);
1452 printf(" type=\"%s\"\n", tmp);
1453 free(tmp);
1454
1455 printf(" transient=%s\n",
1456 quotedBool((pSField->accessFlags & ACC_TRANSIENT) != 0));
1457 printf(" volatile=%s\n",
1458 quotedBool((pSField->accessFlags & ACC_VOLATILE) != 0));
1459 // "value=" not knowable w/o parsing annotations
1460 printf(" static=%s\n",
1461 quotedBool((pSField->accessFlags & ACC_STATIC) != 0));
1462 printf(" final=%s\n",
1463 quotedBool((pSField->accessFlags & ACC_FINAL) != 0));
1464 // "deprecated=" not knowable w/o parsing annotations
1465 printf(" visibility=%s\n",
1466 quotedVisibility(pSField->accessFlags));
1467 printf(">\n</field>\n");
1468 }
1469
1470 free(accessStr);
1471 }
1472
1473 /*
1474 * Dump an instance field.
1475 */
dumpIField(const DexFile * pDexFile,const DexField * pIField,int i)1476 void dumpIField(const DexFile* pDexFile, const DexField* pIField, int i)
1477 {
1478 dumpSField(pDexFile, pIField, i);
1479 }
1480
1481 /*
1482 * Dump the class.
1483 *
1484 * Note "idx" is a DexClassDef index, not a DexTypeId index.
1485 *
1486 * If "*pLastPackage" is NULL or does not match the current class' package,
1487 * the value will be replaced with a newly-allocated string.
1488 */
dumpClass(DexFile * pDexFile,int idx,char ** pLastPackage)1489 void dumpClass(DexFile* pDexFile, int idx, char** pLastPackage)
1490 {
1491 const DexTypeList* pInterfaces;
1492 const DexClassDef* pClassDef;
1493 DexClassData* pClassData = NULL;
1494 const u1* pEncodedData;
1495 const char* fileName;
1496 const char* classDescriptor;
1497 const char* superclassDescriptor;
1498 char* accessStr = NULL;
1499 int i;
1500
1501 pClassDef = dexGetClassDef(pDexFile, idx);
1502
1503 if (gOptions.exportsOnly && (pClassDef->accessFlags & ACC_PUBLIC) == 0) {
1504 //printf("<!-- omitting non-public class %s -->\n",
1505 // classDescriptor);
1506 goto bail;
1507 }
1508
1509 pEncodedData = dexGetClassData(pDexFile, pClassDef);
1510 pClassData = dexReadAndVerifyClassData(&pEncodedData, NULL);
1511
1512 if (pClassData == NULL) {
1513 printf("Trouble reading class data (#%d)\n", idx);
1514 goto bail;
1515 }
1516
1517 classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
1518
1519 /*
1520 * For the XML output, show the package name. Ideally we'd gather
1521 * up the classes, sort them, and dump them alphabetically so the
1522 * package name wouldn't jump around, but that's not a great plan
1523 * for something that needs to run on the device.
1524 */
1525 if (!(classDescriptor[0] == 'L' &&
1526 classDescriptor[strlen(classDescriptor)-1] == ';'))
1527 {
1528 /* arrays and primitives should not be defined explicitly */
1529 fprintf(stderr, "Malformed class name '%s'\n", classDescriptor);
1530 /* keep going? */
1531 } else if (gOptions.outputFormat == OUTPUT_XML) {
1532 char* mangle;
1533 char* lastSlash;
1534 char* cp;
1535
1536 mangle = strdup(classDescriptor + 1);
1537 mangle[strlen(mangle)-1] = '\0';
1538
1539 /* reduce to just the package name */
1540 lastSlash = strrchr(mangle, '/');
1541 if (lastSlash != NULL) {
1542 *lastSlash = '\0';
1543 } else {
1544 *mangle = '\0';
1545 }
1546
1547 for (cp = mangle; *cp != '\0'; cp++) {
1548 if (*cp == '/')
1549 *cp = '.';
1550 }
1551
1552 if (*pLastPackage == NULL || strcmp(mangle, *pLastPackage) != 0) {
1553 /* start of a new package */
1554 if (*pLastPackage != NULL)
1555 printf("</package>\n");
1556 printf("<package name=\"%s\"\n>\n", mangle);
1557 free(*pLastPackage);
1558 *pLastPackage = mangle;
1559 } else {
1560 free(mangle);
1561 }
1562 }
1563
1564 accessStr = createAccessFlagStr(pClassDef->accessFlags, kAccessForClass);
1565
1566 if (pClassDef->superclassIdx == kDexNoIndex) {
1567 superclassDescriptor = NULL;
1568 } else {
1569 superclassDescriptor =
1570 dexStringByTypeIdx(pDexFile, pClassDef->superclassIdx);
1571 }
1572
1573 if (gOptions.outputFormat == OUTPUT_PLAIN) {
1574 printf("Class #%d -\n", idx);
1575 printf(" Class descriptor : '%s'\n", classDescriptor);
1576 printf(" Access flags : 0x%04x (%s)\n",
1577 pClassDef->accessFlags, accessStr);
1578
1579 if (superclassDescriptor != NULL)
1580 printf(" Superclass : '%s'\n", superclassDescriptor);
1581
1582 printf(" Interfaces -\n");
1583 } else {
1584 char* tmp;
1585
1586 tmp = descriptorClassToName(classDescriptor);
1587 printf("<class name=\"%s\"\n", tmp);
1588 free(tmp);
1589
1590 if (superclassDescriptor != NULL) {
1591 tmp = descriptorToDot(superclassDescriptor);
1592 printf(" extends=\"%s\"\n", tmp);
1593 free(tmp);
1594 }
1595 printf(" abstract=%s\n",
1596 quotedBool((pClassDef->accessFlags & ACC_ABSTRACT) != 0));
1597 printf(" static=%s\n",
1598 quotedBool((pClassDef->accessFlags & ACC_STATIC) != 0));
1599 printf(" final=%s\n",
1600 quotedBool((pClassDef->accessFlags & ACC_FINAL) != 0));
1601 // "deprecated=" not knowable w/o parsing annotations
1602 printf(" visibility=%s\n",
1603 quotedVisibility(pClassDef->accessFlags));
1604 printf(">\n");
1605 }
1606 pInterfaces = dexGetInterfacesList(pDexFile, pClassDef);
1607 if (pInterfaces != NULL) {
1608 for (i = 0; i < (int) pInterfaces->size; i++)
1609 dumpInterface(pDexFile, dexGetTypeItem(pInterfaces, i), i);
1610 }
1611
1612 if (gOptions.outputFormat == OUTPUT_PLAIN)
1613 printf(" Static fields -\n");
1614 for (i = 0; i < (int) pClassData->header.staticFieldsSize; i++) {
1615 dumpSField(pDexFile, &pClassData->staticFields[i], i);
1616 }
1617
1618 if (gOptions.outputFormat == OUTPUT_PLAIN)
1619 printf(" Instance fields -\n");
1620 for (i = 0; i < (int) pClassData->header.instanceFieldsSize; i++) {
1621 dumpIField(pDexFile, &pClassData->instanceFields[i], i);
1622 }
1623
1624 if (gOptions.outputFormat == OUTPUT_PLAIN)
1625 printf(" Direct methods -\n");
1626 for (i = 0; i < (int) pClassData->header.directMethodsSize; i++) {
1627 dumpMethod(pDexFile, &pClassData->directMethods[i], i);
1628 }
1629
1630 if (gOptions.outputFormat == OUTPUT_PLAIN)
1631 printf(" Virtual methods -\n");
1632 for (i = 0; i < (int) pClassData->header.virtualMethodsSize; i++) {
1633 dumpMethod(pDexFile, &pClassData->virtualMethods[i], i);
1634 }
1635
1636 // TODO: Annotations.
1637
1638 if (pClassDef->sourceFileIdx != kDexNoIndex)
1639 fileName = dexStringById(pDexFile, pClassDef->sourceFileIdx);
1640 else
1641 fileName = "unknown";
1642
1643 if (gOptions.outputFormat == OUTPUT_PLAIN) {
1644 printf(" source_file_idx : %d (%s)\n",
1645 pClassDef->sourceFileIdx, fileName);
1646 printf("\n");
1647 }
1648
1649 if (gOptions.outputFormat == OUTPUT_XML) {
1650 printf("</class>\n");
1651 }
1652
1653 bail:
1654 free(pClassData);
1655 free(accessStr);
1656 }
1657
1658
1659 /*
1660 * Dump a map in the "differential" format.
1661 *
1662 * TODO: show a hex dump of the compressed data. (We can show the
1663 * uncompressed data if we move the compression code to libdex; otherwise
1664 * it's too complex to merit a fast & fragile implementation here.)
1665 */
dumpDifferentialCompressedMap(const u1 ** pData)1666 void dumpDifferentialCompressedMap(const u1** pData)
1667 {
1668 const u1* data = *pData;
1669 const u1* dataStart = data -1; // format byte already removed
1670 u1 regWidth;
1671 u2 numEntries;
1672
1673 /* standard header */
1674 regWidth = *data++;
1675 numEntries = *data++;
1676 numEntries |= (*data++) << 8;
1677
1678 /* compressed data begins with the compressed data length */
1679 int compressedLen = readUnsignedLeb128(&data);
1680 int addrWidth = 1;
1681 if ((*data & 0x80) != 0)
1682 addrWidth++;
1683
1684 int origLen = 4 + (addrWidth + regWidth) * numEntries;
1685 int compLen = (data - dataStart) + compressedLen;
1686
1687 printf(" (differential compression %d -> %d [%d -> %d])\n",
1688 origLen, compLen,
1689 (addrWidth + regWidth) * numEntries, compressedLen);
1690
1691 /* skip past end of entry */
1692 data += compressedLen;
1693
1694 *pData = data;
1695 }
1696
1697 /*
1698 * Dump register map contents of the current method.
1699 *
1700 * "*pData" should point to the start of the register map data. Advances
1701 * "*pData" to the start of the next map.
1702 */
dumpMethodMap(DexFile * pDexFile,const DexMethod * pDexMethod,int idx,const u1 ** pData)1703 void dumpMethodMap(DexFile* pDexFile, const DexMethod* pDexMethod, int idx,
1704 const u1** pData)
1705 {
1706 const u1* data = *pData;
1707 const DexMethodId* pMethodId;
1708 const char* name;
1709 int offset = data - (u1*) pDexFile->pOptHeader;
1710
1711 pMethodId = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
1712 name = dexStringById(pDexFile, pMethodId->nameIdx);
1713 printf(" #%d: 0x%08x %s\n", idx, offset, name);
1714
1715 u1 format;
1716 int addrWidth;
1717
1718 format = *data++;
1719 if (format == 1) { /* kRegMapFormatNone */
1720 /* no map */
1721 printf(" (no map)\n");
1722 addrWidth = 0;
1723 } else if (format == 2) { /* kRegMapFormatCompact8 */
1724 addrWidth = 1;
1725 } else if (format == 3) { /* kRegMapFormatCompact16 */
1726 addrWidth = 2;
1727 } else if (format == 4) { /* kRegMapFormatDifferential */
1728 dumpDifferentialCompressedMap(&data);
1729 goto bail;
1730 } else {
1731 printf(" (unknown format %d!)\n", format);
1732 /* don't know how to skip data; failure will cascade to end of class */
1733 goto bail;
1734 }
1735
1736 if (addrWidth > 0) {
1737 u1 regWidth;
1738 u2 numEntries;
1739 int idx, addr, byte;
1740
1741 regWidth = *data++;
1742 numEntries = *data++;
1743 numEntries |= (*data++) << 8;
1744
1745 for (idx = 0; idx < numEntries; idx++) {
1746 addr = *data++;
1747 if (addrWidth > 1)
1748 addr |= (*data++) << 8;
1749
1750 printf(" %4x:", addr);
1751 for (byte = 0; byte < regWidth; byte++) {
1752 printf(" %02x", *data++);
1753 }
1754 printf("\n");
1755 }
1756 }
1757
1758 bail:
1759 //if (addrWidth >= 0)
1760 // *pData = align32(data);
1761 *pData = data;
1762 }
1763
1764 /*
1765 * Dump the contents of the register map area.
1766 *
1767 * These are only present in optimized DEX files, and the structure is
1768 * not really exposed to other parts of the VM itself. We're going to
1769 * dig through them here, but this is pretty fragile. DO NOT rely on
1770 * this or derive other code from it.
1771 */
dumpRegisterMaps(DexFile * pDexFile)1772 void dumpRegisterMaps(DexFile* pDexFile)
1773 {
1774 const u1* pClassPool = (const u1*)pDexFile->pRegisterMapPool;
1775 const u4* classOffsets;
1776 const u1* ptr;
1777 u4 numClasses;
1778 int baseFileOffset = (u1*) pClassPool - (u1*) pDexFile->pOptHeader;
1779 int idx;
1780
1781 if (pClassPool == NULL) {
1782 printf("No register maps found\n");
1783 return;
1784 }
1785
1786 ptr = pClassPool;
1787 numClasses = get4LE(ptr);
1788 ptr += sizeof(u4);
1789 classOffsets = (const u4*) ptr;
1790
1791 printf("RMAP begins at offset 0x%07x\n", baseFileOffset);
1792 printf("Maps for %d classes\n", numClasses);
1793 for (idx = 0; idx < (int) numClasses; idx++) {
1794 const DexClassDef* pClassDef;
1795 const char* classDescriptor;
1796
1797 pClassDef = dexGetClassDef(pDexFile, idx);
1798 classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
1799
1800 printf("%4d: +%d (0x%08x) %s\n", idx, classOffsets[idx],
1801 baseFileOffset + classOffsets[idx], classDescriptor);
1802
1803 if (classOffsets[idx] == 0)
1804 continue;
1805
1806 /*
1807 * What follows is a series of RegisterMap entries, one for every
1808 * direct method, then one for every virtual method.
1809 */
1810 DexClassData* pClassData;
1811 const u1* pEncodedData;
1812 const u1* data = (u1*) pClassPool + classOffsets[idx];
1813 u2 methodCount;
1814 int i;
1815
1816 pEncodedData = dexGetClassData(pDexFile, pClassDef);
1817 pClassData = dexReadAndVerifyClassData(&pEncodedData, NULL);
1818 if (pClassData == NULL) {
1819 fprintf(stderr, "Trouble reading class data\n");
1820 continue;
1821 }
1822
1823 methodCount = *data++;
1824 methodCount |= (*data++) << 8;
1825 data += 2; /* two pad bytes follow methodCount */
1826 if (methodCount != pClassData->header.directMethodsSize
1827 + pClassData->header.virtualMethodsSize)
1828 {
1829 printf("NOTE: method count discrepancy (%d != %d + %d)\n",
1830 methodCount, pClassData->header.directMethodsSize,
1831 pClassData->header.virtualMethodsSize);
1832 /* this is bad, but keep going anyway */
1833 }
1834
1835 printf(" direct methods: %d\n",
1836 pClassData->header.directMethodsSize);
1837 for (i = 0; i < (int) pClassData->header.directMethodsSize; i++) {
1838 dumpMethodMap(pDexFile, &pClassData->directMethods[i], i, &data);
1839 }
1840
1841 printf(" virtual methods: %d\n",
1842 pClassData->header.virtualMethodsSize);
1843 for (i = 0; i < (int) pClassData->header.virtualMethodsSize; i++) {
1844 dumpMethodMap(pDexFile, &pClassData->virtualMethods[i], i, &data);
1845 }
1846
1847 free(pClassData);
1848 }
1849 }
1850
findMapItem(const DexFile * pDexFile,u4 type)1851 static const DexMapItem* findMapItem(const DexFile* pDexFile, u4 type)
1852 {
1853 const u4 offset = pDexFile->pHeader->mapOff;
1854 const DexMapList* list = (const DexMapList*)(pDexFile->baseAddr + offset);
1855 for (u4 i = 0; i < list->size; ++i) {
1856 if (list->list[i].type == type) {
1857 return &list->list[i];
1858 }
1859 }
1860 return nullptr;
1861 }
1862
dumpMethodHandles(DexFile * pDexFile)1863 static void dumpMethodHandles(DexFile* pDexFile)
1864 {
1865 const DexMapItem* item = findMapItem(pDexFile, kDexTypeMethodHandleItem);
1866 if (item == nullptr) return;
1867 const DexMethodHandleItem* method_handles =
1868 (const DexMethodHandleItem*)(pDexFile->baseAddr + item->offset);
1869 for (u4 i = 0; i < item->size; ++i) {
1870 const DexMethodHandleItem& mh = method_handles[i];
1871 const char* type;
1872 bool is_invoke;
1873 bool is_static;
1874 switch ((MethodHandleType) mh.methodHandleType) {
1875 case MethodHandleType::STATIC_PUT:
1876 type = "put-static";
1877 is_invoke = false;
1878 is_static = true;
1879 break;
1880 case MethodHandleType::STATIC_GET:
1881 type = "get-static";
1882 is_invoke = false;
1883 is_static = true;
1884 break;
1885 case MethodHandleType::INSTANCE_PUT:
1886 type = "put-instance";
1887 is_invoke = false;
1888 is_static = false;
1889 break;
1890 case MethodHandleType::INSTANCE_GET:
1891 type = "get-instance";
1892 is_invoke = false;
1893 is_static = false;
1894 break;
1895 case MethodHandleType::INVOKE_STATIC:
1896 type = "invoke-static";
1897 is_invoke = true;
1898 is_static = true;
1899 break;
1900 case MethodHandleType::INVOKE_INSTANCE:
1901 type = "invoke-instance";
1902 is_invoke = true;
1903 is_static = false;
1904 break;
1905 case MethodHandleType::INVOKE_CONSTRUCTOR:
1906 type = "invoke-constructor";
1907 is_invoke = true;
1908 is_static = false;
1909 break;
1910 case MethodHandleType::INVOKE_DIRECT:
1911 type = "invoke-direct";
1912 is_invoke = true;
1913 is_static = false;
1914 break;
1915 case MethodHandleType::INVOKE_INTERFACE:
1916 type = "invoke-interface";
1917 is_invoke = true;
1918 is_static = false;
1919 break;
1920 default:
1921 printf("Unknown method handle type 0x%02x, skipped.", mh.methodHandleType);
1922 continue;
1923 }
1924
1925 FieldMethodInfo info;
1926 if (is_invoke) {
1927 if (!getMethodInfo(pDexFile, mh.fieldOrMethodIdx, &info)) {
1928 printf("Unknown method handle target method@%04x, skipped.", mh.fieldOrMethodIdx);
1929 continue;
1930 }
1931 } else {
1932 if (!getFieldInfo(pDexFile, mh.fieldOrMethodIdx, &info)) {
1933 printf("Unknown method handle target field@%04x, skipped.", mh.fieldOrMethodIdx);
1934 continue;
1935 }
1936 }
1937
1938 const char* instance = is_static ? "" : info.classDescriptor;
1939
1940 if (gOptions.outputFormat == OUTPUT_XML) {
1941 printf("<method_handle index index=\"%u\"\n", i);
1942 printf(" type=\"%s\"\n", type);
1943 printf(" target_class=\"%s\"\n", info.classDescriptor);
1944 printf(" target_member=\"%s\"\n", info.name);
1945 printf(" target_member_type=\"%c%s%s\"\n",
1946 info.signature[0], instance, info.signature + 1);
1947 printf("</method_handle>\n");
1948 } else {
1949 printf("Method Handle #%u:\n", i);
1950 printf(" type : %s\n", type);
1951 printf(" target : %s %s\n", info.classDescriptor, info.name);
1952 printf(" target_type : %c%s%s\n", info.signature[0], instance, info.signature + 1);
1953 }
1954 }
1955 }
1956
1957 /* Helper for dumpCallSites(), which reads a 1- to 8- byte signed
1958 * little endian value. */
readSignedLittleEndian(const u1 ** pData,u4 size)1959 static u8 readSignedLittleEndian(const u1** pData, u4 size) {
1960 const u1* data = *pData;
1961 u8 result = 0;
1962 u4 i;
1963
1964 for (i = 0; i < size; i++) {
1965 result = (result >> 8) | (((int64_t)*data++) << 56);
1966 }
1967
1968 result >>= (8 - size) * 8;
1969 *pData = data;
1970 return result;
1971 }
1972
1973 /* Helper for dumpCallSites(), which reads a 1- to 8- byte unsigned
1974 * little endian value. */
readUnsignedLittleEndian(const u1 ** pData,u4 size,bool fillOnRight=false)1975 static u8 readUnsignedLittleEndian(const u1** pData, u4 size, bool fillOnRight = false) {
1976 const u1* data = *pData;
1977 u8 result = 0;
1978 u4 i;
1979
1980 for (i = 0; i < size; i++) {
1981 result = (result >> 8) | (((u8)*data++) << 56);
1982 }
1983
1984 if (!fillOnRight) {
1985 result >>= (8u - size) * 8;
1986 }
1987
1988 *pData = data;
1989 return result;
1990 }
1991
dumpCallSites(DexFile * pDexFile)1992 static void dumpCallSites(DexFile* pDexFile)
1993 {
1994 const DexMapItem* item = findMapItem(pDexFile, kDexTypeCallSiteIdItem);
1995 if (item == nullptr) return;
1996 const DexCallSiteId* ids = (const DexCallSiteId*)(pDexFile->baseAddr + item->offset);
1997 for (u4 index = 0; index < item->size; ++index) {
1998 bool doXml = (gOptions.outputFormat == OUTPUT_XML);
1999 printf(doXml ? "<call_site index=\"%u\" offset=\"%u\">\n" : "Call Site #%u // offset %u\n",
2000 index, ids[index].callSiteOff);
2001 const u1* data = pDexFile->baseAddr + ids[index].callSiteOff;
2002 u4 count = readUnsignedLeb128(&data);
2003 for (u4 i = 0; i < count; ++i) {
2004 printf(doXml ? "<link_argument index=\"%u\" " : " link_argument[%u] : ", i);
2005 u1 headerByte = *data++;
2006 u4 valueType = headerByte & kDexAnnotationValueTypeMask;
2007 u4 valueArg = headerByte >> kDexAnnotationValueArgShift;
2008 switch (valueType) {
2009 case kDexAnnotationByte: {
2010 printf(doXml ? "type=\"byte\" value=\"%d\"/>" : "%d (byte)", (int)*data++);
2011 break;
2012 }
2013 case kDexAnnotationShort: {
2014 printf(doXml ? "type=\"short\" value=\"%d\"/>" : "%d (short)",
2015 (int) readSignedLittleEndian(&data, valueArg + 1));
2016 break;
2017 }
2018 case kDexAnnotationChar: {
2019 printf(doXml ? "type=\"short\" value=\"%u\"/>" : "%u (char)",
2020 (u2) readUnsignedLittleEndian(&data, valueArg + 1));
2021 break;
2022 }
2023 case kDexAnnotationInt: {
2024 printf(doXml ? "type=\"int\" value=\"%d\"/>" : "%d (int)",
2025 (int) readSignedLittleEndian(&data, valueArg + 1));
2026 break;
2027 }
2028 case kDexAnnotationLong: {
2029 printf(doXml ? "type=\"long\" value=\"%" PRId64 "\"/>" : "%" PRId64 " (long)",
2030 (int64_t) readSignedLittleEndian(&data, valueArg + 1));
2031 break;
2032 }
2033 case kDexAnnotationFloat: {
2034 u4 rawValue = (u4) (readUnsignedLittleEndian(&data, valueArg + 1, true) >> 32);
2035 printf(doXml ? "type=\"float\" value=\"%g\"/>" : "%g (float)",
2036 *((float*) &rawValue));
2037 break;
2038 }
2039 case kDexAnnotationDouble: {
2040 u8 rawValue = readUnsignedLittleEndian(&data, valueArg + 1, true);
2041 printf(doXml ? "type=\"double\" value=\"%g\"/>" : "%g (double)",
2042 *((double*) &rawValue));
2043 break;
2044 }
2045 case kDexAnnotationMethodType: {
2046 u4 idx = (u4) readUnsignedLittleEndian(&data, valueArg + 1);
2047 ProtoInfo protoInfo;
2048 memset(&protoInfo, 0, sizeof(protoInfo));
2049 getProtoInfo(pDexFile, idx, &protoInfo);
2050 printf(doXml ? "type=\"MethodType\" value=\"(%s)%s\"/>" : "(%s)%s (MethodType)",
2051 protoInfo.parameterTypes, protoInfo.returnType);
2052 free(protoInfo.parameterTypes);
2053 break;
2054 }
2055 case kDexAnnotationMethodHandle: {
2056 u4 idx = (u4) readUnsignedLittleEndian(&data, valueArg + 1);
2057 printf(doXml ? "type=\"MethodHandle\" value=\"%u\"/>" : "%u (MethodHandle)",
2058 idx);
2059 break;
2060 }
2061 case kDexAnnotationString: {
2062 u4 idx = (u4) readUnsignedLittleEndian(&data, valueArg + 1);
2063 printf(doXml ? "type=\"String\" value=\"%s\"/>" : "%s (String)",
2064 dexStringById(pDexFile, idx));
2065 break;
2066 }
2067 case kDexAnnotationType: {
2068 u4 idx = (u4) readUnsignedLittleEndian(&data, valueArg + 1);
2069 printf(doXml ? "type=\"Class\" value=\"%s\"/>" : "%s (Class)",
2070 dexStringByTypeIdx(pDexFile, idx));
2071 break;
2072 }
2073 case kDexAnnotationNull: {
2074 printf(doXml ? "type=\"null\" value=\"null\"/>" : "null (null)");
2075 break;
2076 }
2077 case kDexAnnotationBoolean: {
2078 printf(doXml ? "type=\"boolean\" value=\"%s\"/>" : "%s (boolean)",
2079 (valueArg & 1) == 0 ? "false" : "true");
2080 break;
2081 }
2082 default:
2083 // Other types are not anticipated being reached here.
2084 printf("Unexpected type found, bailing on call site info.\n");
2085 i = count;
2086 break;
2087 }
2088 printf("\n");
2089 }
2090
2091 if (doXml) {
2092 printf("</callsite>\n");
2093 }
2094 }
2095 }
2096
2097 /*
2098 * Dump the requested sections of the file.
2099 */
processDexFile(const char * fileName,DexFile * pDexFile)2100 void processDexFile(const char* fileName, DexFile* pDexFile)
2101 {
2102 char* package = NULL;
2103 int i;
2104
2105 if (gOptions.verbose) {
2106 printf("Opened '%s', DEX version '%.3s'\n", fileName,
2107 pDexFile->pHeader->magic +4);
2108 }
2109
2110 if (gOptions.dumpRegisterMaps) {
2111 dumpRegisterMaps(pDexFile);
2112 return;
2113 }
2114
2115 if (gOptions.showFileHeaders) {
2116 dumpFileHeader(pDexFile);
2117 dumpOptDirectory(pDexFile);
2118 }
2119
2120 if (gOptions.outputFormat == OUTPUT_XML)
2121 printf("<api>\n");
2122
2123 for (i = 0; i < (int) pDexFile->pHeader->classDefsSize; i++) {
2124 if (gOptions.showSectionHeaders)
2125 dumpClassDef(pDexFile, i);
2126
2127 dumpClass(pDexFile, i, &package);
2128 }
2129
2130 dumpMethodHandles(pDexFile);
2131 dumpCallSites(pDexFile);
2132
2133 /* free the last one allocated */
2134 if (package != NULL) {
2135 printf("</package>\n");
2136 free(package);
2137 }
2138
2139 if (gOptions.outputFormat == OUTPUT_XML)
2140 printf("</api>\n");
2141 }
2142
2143
2144 /*
2145 * Process one file.
2146 */
process(const char * fileName)2147 int process(const char* fileName)
2148 {
2149 DexFile* pDexFile = NULL;
2150 MemMapping map;
2151 bool mapped = false;
2152 int result = -1;
2153
2154 if (gOptions.verbose)
2155 printf("Processing '%s'...\n", fileName);
2156
2157 if (dexOpenAndMap(fileName, gOptions.tempFileName, &map, false) != 0) {
2158 return result;
2159 }
2160 mapped = true;
2161
2162 int flags = kDexParseVerifyChecksum;
2163 if (gOptions.ignoreBadChecksum)
2164 flags |= kDexParseContinueOnError;
2165
2166 pDexFile = dexFileParse((u1*)map.addr, map.length, flags);
2167 if (pDexFile == NULL) {
2168 fprintf(stderr, "ERROR: DEX parse failed\n");
2169 goto bail;
2170 }
2171
2172 if (gOptions.checksumOnly) {
2173 printf("Checksum verified\n");
2174 } else {
2175 processDexFile(fileName, pDexFile);
2176 }
2177
2178 result = 0;
2179
2180 bail:
2181 if (mapped)
2182 sysReleaseShmem(&map);
2183 if (pDexFile != NULL)
2184 dexFileFree(pDexFile);
2185 return result;
2186 }
2187
2188
2189 /*
2190 * Show usage.
2191 */
usage(void)2192 void usage(void)
2193 {
2194 fprintf(stderr, "Copyright (C) 2007 The Android Open Source Project\n\n");
2195 fprintf(stderr,
2196 "%s: [-c] [-d] [-f] [-h] [-i] [-l layout] [-m] [-t tempfile] dexfile...\n",
2197 gProgName);
2198 fprintf(stderr, "\n");
2199 fprintf(stderr, " -c : verify checksum and exit\n");
2200 fprintf(stderr, " -d : disassemble code sections\n");
2201 fprintf(stderr, " -f : display summary information from file header\n");
2202 fprintf(stderr, " -h : display file header details\n");
2203 fprintf(stderr, " -i : ignore checksum failures\n");
2204 fprintf(stderr, " -l : output layout, either 'plain' or 'xml'\n");
2205 fprintf(stderr, " -m : dump register maps (and nothing else)\n");
2206 fprintf(stderr, " -t : temp file name (defaults to /sdcard/dex-temp-*)\n");
2207 }
2208
2209 /*
2210 * Parse args.
2211 *
2212 * I'm not using getopt_long() because we may not have it in libc.
2213 */
main(int argc,char * const argv[])2214 int main(int argc, char* const argv[])
2215 {
2216 bool wantUsage = false;
2217 int ic;
2218
2219 memset(&gOptions, 0, sizeof(gOptions));
2220 gOptions.verbose = true;
2221
2222 while (1) {
2223 ic = getopt(argc, argv, "cdfhil:mt:");
2224 if (ic < 0)
2225 break;
2226
2227 switch (ic) {
2228 case 'c': // verify the checksum then exit
2229 gOptions.checksumOnly = true;
2230 break;
2231 case 'd': // disassemble Dalvik instructions
2232 gOptions.disassemble = true;
2233 break;
2234 case 'f': // dump outer file header
2235 gOptions.showFileHeaders = true;
2236 break;
2237 case 'h': // dump section headers, i.e. all meta-data
2238 gOptions.showSectionHeaders = true;
2239 break;
2240 case 'i': // continue even if checksum is bad
2241 gOptions.ignoreBadChecksum = true;
2242 break;
2243 case 'l': // layout
2244 if (strcmp(optarg, "plain") == 0) {
2245 gOptions.outputFormat = OUTPUT_PLAIN;
2246 } else if (strcmp(optarg, "xml") == 0) {
2247 gOptions.outputFormat = OUTPUT_XML;
2248 gOptions.verbose = false;
2249 gOptions.exportsOnly = true;
2250 } else {
2251 wantUsage = true;
2252 }
2253 break;
2254 case 'm': // dump register maps only
2255 gOptions.dumpRegisterMaps = true;
2256 break;
2257 case 't': // temp file, used when opening compressed Jar
2258 gOptions.tempFileName = optarg;
2259 break;
2260 default:
2261 wantUsage = true;
2262 break;
2263 }
2264 }
2265
2266 if (optind == argc) {
2267 fprintf(stderr, "%s: no file specified\n", gProgName);
2268 wantUsage = true;
2269 }
2270
2271 if (gOptions.checksumOnly && gOptions.ignoreBadChecksum) {
2272 fprintf(stderr, "Can't specify both -c and -i\n");
2273 wantUsage = true;
2274 }
2275
2276 if (wantUsage) {
2277 usage();
2278 return 2;
2279 }
2280
2281 int result = 0;
2282 while (optind < argc) {
2283 result |= process(argv[optind++]);
2284 }
2285
2286 return (result != 0);
2287 }
2288