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