blob: 09b072efb0e55e83ec7a1173db51c8987de5bcee [file] [log] [blame]
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001/*
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 */
The Android Open Source Project99409882009-03-18 22:20:24 -070016
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080017/*
18 * The "dexdump" tool is intended to mimic "objdump". When possible, use
19 * similar command-line arguments.
20 *
Andy McFaddena2ee53b2009-05-05 16:52:10 -070021 * 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
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080031 */
32#include "libdex/DexFile.h"
33#include "libdex/DexCatch.h"
34#include "libdex/DexClass.h"
35#include "libdex/DexProto.h"
36#include "libdex/InstrUtils.h"
37#include "libdex/SysUtil.h"
38#include "libdex/CmdUtils.h"
39
40#include "dexdump/OpCodeNames.h"
41
42#include <stdlib.h>
43#include <stdio.h>
44#include <fcntl.h>
45#include <string.h>
46#include <unistd.h>
47#include <getopt.h>
48#include <errno.h>
49#include <assert.h>
50
51static const char* gProgName = "dexdump";
52
53static InstructionWidth* gInstrWidth;
54static InstructionFormat* gInstrFormat;
55
Andy McFaddena2ee53b2009-05-05 16:52:10 -070056typedef enum OutputFormat {
57 OUTPUT_PLAIN = 0, /* default */
58 OUTPUT_XML, /* fancy */
59} OutputFormat;
60
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080061/* command-line options */
62struct {
Andy McFadden0198b142009-04-02 14:48:56 -070063 bool checksumOnly;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080064 bool disassemble;
65 bool showFileHeaders;
66 bool showSectionHeaders;
Andy McFadden2124cb82009-03-25 15:37:39 -070067 bool ignoreBadChecksum;
The Android Open Source Project99409882009-03-18 22:20:24 -070068 bool dumpRegisterMaps;
Andy McFaddena2ee53b2009-05-05 16:52:10 -070069 OutputFormat outputFormat;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080070 const char* tempFileName;
Andy McFaddena2ee53b2009-05-05 16:52:10 -070071 bool exportsOnly;
72 bool verbose;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080073} gOptions;
74
75/* basic info about a field or method */
76typedef struct FieldMethodInfo {
77 const char* classDescriptor;
78 const char* name;
79 const char* signature;
80} FieldMethodInfo;
81
82/*
83 * Get 2 little-endian bytes.
84 */
85static inline u2 get2LE(unsigned char const* pSrc)
86{
87 return pSrc[0] | (pSrc[1] << 8);
88}
89
90/*
The Android Open Source Project99409882009-03-18 22:20:24 -070091 * Get 4 little-endian bytes.
92 */
93static inline u4 get4LE(unsigned char const* pSrc)
94{
95 return pSrc[0] | (pSrc[1] << 8) | (pSrc[2] << 16) | (pSrc[3] << 24);
96}
97
98/*
Andy McFaddena2ee53b2009-05-05 16:52:10 -070099 * Converts a single-character primitive type into its human-readable
100 * equivalent.
101 */
102static const char* primitiveTypeLabel(char typeChar)
103{
104 switch (typeChar) {
105 case 'B': return "byte";
106 case 'C': return "char";
107 case 'D': return "double";
108 case 'F': return "float";
109 case 'I': return "int";
110 case 'J': return "long";
111 case 'S': return "short";
112 case 'V': return "void";
113 case 'Z': return "boolean";
114 default:
115 return "UNKNOWN";
116 }
117}
118
119/*
120 * Converts a type descriptor to human-readable "dotted" form. For
121 * example, "Ljava/lang/String;" becomes "java.lang.String", and
122 * "[I" becomes "int[]". Also converts '$' to '.', which means this
123 * form can't be converted back to a descriptor.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800124 */
125static char* descriptorToDot(const char* str)
126{
Andy McFaddena2ee53b2009-05-05 16:52:10 -0700127 int targetLen = strlen(str);
128 int offset = 0;
129 int arrayDepth = 0;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800130 char* newStr;
131
Andy McFaddena2ee53b2009-05-05 16:52:10 -0700132 /* strip leading [s; will be added to end */
133 while (targetLen > 1 && str[offset] == '[') {
134 offset++;
135 targetLen--;
136 }
137 arrayDepth = offset;
138
139 if (targetLen == 1) {
140 /* primitive type */
141 str = primitiveTypeLabel(str[offset]);
142 offset = 0;
143 targetLen = strlen(str);
144 } else {
145 /* account for leading 'L' and trailing ';' */
146 if (targetLen >= 2 && str[offset] == 'L' &&
147 str[offset+targetLen-1] == ';')
148 {
149 targetLen -= 2;
150 offset++;
151 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800152 }
153
Andy McFaddena2ee53b2009-05-05 16:52:10 -0700154 newStr = malloc(targetLen + arrayDepth * 2 +1);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800155
Andy McFaddena2ee53b2009-05-05 16:52:10 -0700156 /* copy class name over */
157 int i;
158 for (i = 0; i < targetLen; i++) {
159 char ch = str[offset + i];
160 newStr[i] = (ch == '/' || ch == '$') ? '.' : ch;
161 }
162
163 /* add the appropriate number of brackets for arrays */
164 while (arrayDepth-- > 0) {
165 newStr[i++] = '[';
166 newStr[i++] = ']';
167 }
168 newStr[i] = '\0';
169 assert(i == targetLen + arrayDepth * 2);
170
171 return newStr;
172}
173
174/*
175 * Converts the class name portion of a type descriptor to human-readable
176 * "dotted" form.
177 *
178 * Returns a newly-allocated string.
179 */
180static char* descriptorClassToDot(const char* str)
181{
182 const char* lastSlash;
183 char* newStr;
184 char* cp;
185
186 /* reduce to just the class name, trimming trailing ';' */
187 lastSlash = strrchr(str, '/');
188 if (lastSlash == NULL)
189 lastSlash = str + 1; /* start past 'L' */
190 else
191 lastSlash++; /* start past '/' */
192
193 newStr = strdup(lastSlash);
194 newStr[strlen(lastSlash)-1] = '\0';
195 for (cp = newStr; *cp != '\0'; cp++) {
196 if (*cp == '$')
197 *cp = '.';
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800198 }
199
200 return newStr;
201}
202
203/*
Andy McFaddena2ee53b2009-05-05 16:52:10 -0700204 * Returns a quoted string representing the boolean value.
205 */
206static const char* quotedBool(bool val)
207{
208 if (val)
209 return "\"true\"";
210 else
211 return "\"false\"";
212}
213
214static const char* quotedVisibility(u4 accessFlags)
215{
216 if ((accessFlags & ACC_PUBLIC) != 0)
217 return "\"public\"";
218 else if ((accessFlags & ACC_PROTECTED) != 0)
219 return "\"protected\"";
220 else if ((accessFlags & ACC_PRIVATE) != 0)
221 return "\"private\"";
222 else
223 return "\"package\"";
224}
225
226/*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800227 * Count the number of '1' bits in a word.
228 *
229 * Having completed this, I'm ready for an interview at Google.
230 *
231 * TODO? there's a parallel version w/o loops. Performance not currently
232 * important.
233 */
234static int countOnes(u4 val)
235{
236 int count = 0;
237
238 while (val != 0) {
239 val &= val-1;
240 count++;
241 }
242
243 return count;
244}
245
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800246/*
247 * Flag for use with createAccessFlagStr().
248 */
249typedef enum AccessFor {
250 kAccessForClass = 0, kAccessForMethod = 1, kAccessForField = 2,
251 kAccessForMAX
252} AccessFor;
253
254/*
255 * Create a new string with human-readable access flags.
256 *
257 * In the base language the access_flags fields are type u2; in Dalvik
258 * they're u4.
259 */
260static char* createAccessFlagStr(u4 flags, AccessFor forWhat)
261{
262#define NUM_FLAGS 18
263 static const char* kAccessStrings[kAccessForMAX][NUM_FLAGS] = {
264 {
265 /* class, inner class */
266 "PUBLIC", /* 0x0001 */
267 "PRIVATE", /* 0x0002 */
268 "PROTECTED", /* 0x0004 */
269 "STATIC", /* 0x0008 */
270 "FINAL", /* 0x0010 */
271 "?", /* 0x0020 */
272 "?", /* 0x0040 */
273 "?", /* 0x0080 */
274 "?", /* 0x0100 */
275 "INTERFACE", /* 0x0200 */
276 "ABSTRACT", /* 0x0400 */
277 "?", /* 0x0800 */
278 "SYNTHETIC", /* 0x1000 */
279 "ANNOTATION", /* 0x2000 */
280 "ENUM", /* 0x4000 */
281 "?", /* 0x8000 */
282 "VERIFIED", /* 0x10000 */
283 "OPTIMIZED", /* 0x20000 */
284 },
285 {
286 /* method */
287 "PUBLIC", /* 0x0001 */
288 "PRIVATE", /* 0x0002 */
289 "PROTECTED", /* 0x0004 */
290 "STATIC", /* 0x0008 */
291 "FINAL", /* 0x0010 */
292 "SYNCHRONIZED", /* 0x0020 */
293 "BRIDGE", /* 0x0040 */
294 "VARARGS", /* 0x0080 */
295 "NATIVE", /* 0x0100 */
296 "?", /* 0x0200 */
297 "ABSTRACT", /* 0x0400 */
298 "STRICT", /* 0x0800 */
299 "SYNTHETIC", /* 0x1000 */
300 "?", /* 0x2000 */
301 "?", /* 0x4000 */
302 "MIRANDA", /* 0x8000 */
303 "CONSTRUCTOR", /* 0x10000 */
304 "DECLARED_SYNCHRONIZED", /* 0x20000 */
305 },
306 {
307 /* field */
308 "PUBLIC", /* 0x0001 */
309 "PRIVATE", /* 0x0002 */
310 "PROTECTED", /* 0x0004 */
311 "STATIC", /* 0x0008 */
312 "FINAL", /* 0x0010 */
313 "?", /* 0x0020 */
314 "VOLATILE", /* 0x0040 */
315 "TRANSIENT", /* 0x0080 */
316 "?", /* 0x0100 */
317 "?", /* 0x0200 */
318 "?", /* 0x0400 */
319 "?", /* 0x0800 */
320 "SYNTHETIC", /* 0x1000 */
321 "?", /* 0x2000 */
322 "ENUM", /* 0x4000 */
323 "?", /* 0x8000 */
324 "?", /* 0x10000 */
325 "?", /* 0x20000 */
326 },
327 };
328 const int kLongest = 21; /* strlen of longest string above */
329 int i, count;
330 char* str;
331 char* cp;
332
333 /*
334 * Allocate enough storage to hold the expected number of strings,
335 * plus a space between each. We over-allocate, using the longest
336 * string above as the base metric.
337 */
338 count = countOnes(flags);
339 cp = str = (char*) malloc(count * (kLongest+1) +1);
340
341 for (i = 0; i < NUM_FLAGS; i++) {
342 if (flags & 0x01) {
343 const char* accessStr = kAccessStrings[forWhat][i];
344 int len = strlen(accessStr);
345 if (cp != str)
346 *cp++ = ' ';
347
348 memcpy(cp, accessStr, len);
349 cp += len;
350 }
351 flags >>= 1;
352 }
353 *cp = '\0';
354
355 return str;
356}
357
358
359/*
Andy McFadden0ea77b92010-04-20 14:18:59 -0700360 * Copy character data from "data" to "out", converting non-ASCII values
361 * to printf format chars or an ASCII filler ('.' or '?').
362 *
363 * The output buffer must be able to hold (2*len)+1 bytes. The result is
364 * NUL-terminated.
365 */
366static void asciify(char* out, const unsigned char* data, size_t len)
367{
368 while (len--) {
369 if (*data < 0x20) {
370 /* could do more here, but we don't need them yet */
371 switch (*data) {
372 case '\0':
373 *out++ = '\\';
374 *out++ = '0';
375 break;
376 case '\n':
377 *out++ = '\\';
378 *out++ = 'n';
379 break;
380 default:
381 *out++ = '.';
382 break;
383 }
384 } else if (*data >= 0x80) {
385 *out++ = '?';
386 } else {
387 *out++ = *data;
388 }
389 data++;
390 }
391 *out = '\0';
392}
393
394/*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800395 * Dump the file header.
396 */
397void dumpFileHeader(const DexFile* pDexFile)
398{
Andy McFadden0ea77b92010-04-20 14:18:59 -0700399 const DexOptHeader* pOptHeader = pDexFile->pOptHeader;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800400 const DexHeader* pHeader = pDexFile->pHeader;
Andy McFadden0ea77b92010-04-20 14:18:59 -0700401 char sanitized[sizeof(pHeader->magic)*2 +1];
402
403 assert(sizeof(pHeader->magic) == sizeof(pOptHeader->magic));
404
405 if (pOptHeader != NULL) {
406 printf("Optimized DEX file header:\n");
407
408 asciify(sanitized, pOptHeader->magic, sizeof(pOptHeader->magic));
409 printf("magic : '%s'\n", sanitized);
410 printf("dex_offset : %d (0x%06x)\n",
411 pOptHeader->dexOffset, pOptHeader->dexOffset);
412 printf("dex_length : %d\n", pOptHeader->dexLength);
413 printf("deps_offset : %d (0x%06x)\n",
414 pOptHeader->depsOffset, pOptHeader->depsOffset);
415 printf("deps_length : %d\n", pOptHeader->depsLength);
416 printf("aux_offset : %d (0x%06x)\n",
417 pOptHeader->auxOffset, pOptHeader->auxOffset);
418 printf("aux_length : %d\n", pOptHeader->auxLength);
419 printf("flags : %08x\n", pOptHeader->flags);
420 printf("checksum : %08x\n", pOptHeader->checksum);
421 printf("\n");
422 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800423
424 printf("DEX file header:\n");
Andy McFadden0ea77b92010-04-20 14:18:59 -0700425 asciify(sanitized, pHeader->magic, sizeof(pHeader->magic));
426 printf("magic : '%s'\n", sanitized);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800427 printf("checksum : %08x\n", pHeader->checksum);
428 printf("signature : %02x%02x...%02x%02x\n",
429 pHeader->signature[0], pHeader->signature[1],
430 pHeader->signature[kSHA1DigestLen-2],
431 pHeader->signature[kSHA1DigestLen-1]);
432 printf("file_size : %d\n", pHeader->fileSize);
433 printf("header_size : %d\n", pHeader->headerSize);
434 printf("link_size : %d\n", pHeader->linkSize);
435 printf("link_off : %d (0x%06x)\n",
436 pHeader->linkOff, pHeader->linkOff);
437 printf("string_ids_size : %d\n", pHeader->stringIdsSize);
438 printf("string_ids_off : %d (0x%06x)\n",
439 pHeader->stringIdsOff, pHeader->stringIdsOff);
440 printf("type_ids_size : %d\n", pHeader->typeIdsSize);
441 printf("type_ids_off : %d (0x%06x)\n",
442 pHeader->typeIdsOff, pHeader->typeIdsOff);
443 printf("field_ids_size : %d\n", pHeader->fieldIdsSize);
444 printf("field_ids_off : %d (0x%06x)\n",
445 pHeader->fieldIdsOff, pHeader->fieldIdsOff);
446 printf("method_ids_size : %d\n", pHeader->methodIdsSize);
447 printf("method_ids_off : %d (0x%06x)\n",
448 pHeader->methodIdsOff, pHeader->methodIdsOff);
449 printf("class_defs_size : %d\n", pHeader->classDefsSize);
450 printf("class_defs_off : %d (0x%06x)\n",
451 pHeader->classDefsOff, pHeader->classDefsOff);
452 printf("data_size : %d\n", pHeader->dataSize);
453 printf("data_off : %d (0x%06x)\n",
454 pHeader->dataOff, pHeader->dataOff);
455 printf("\n");
456}
457
458/*
Andy McFadden0ea77b92010-04-20 14:18:59 -0700459 * Dump the "table of contents" for the aux area.
460 */
461void dumpAuxDirectory(const DexFile* pDexFile)
462{
463 const DexOptHeader* pOptHeader = pDexFile->pOptHeader;
464 if (pOptHeader == NULL)
465 return;
466
467 printf("AUX section contents:\n");
468
469 const u4* pAux = (const u4*) ((u1*) pOptHeader + pOptHeader->auxOffset);
470
471 if (*pAux == 0) {
472 printf("(1.0 format, only class lookup table is present)\n\n");
473 return;
474 }
475
476 /*
477 * The "aux" section is in "chunk" format: a 32-bit identifier, a 32-bit
478 * length, then the data. Chunks start on 64-bit boundaries.
479 */
480 while (*pAux != kDexChunkEnd) {
481 const char* verboseStr;
482
483 u4 size = *(pAux+1);
484
485 switch (*pAux) {
486 case kDexChunkClassLookup:
487 verboseStr = "class lookup hash table";
488 break;
489 case kDexChunkRegisterMaps:
490 verboseStr = "register maps";
491 break;
492 case kDexChunkReducingIndexMap:
493 verboseStr = "'reducing' index map";
494 break;
495 case kDexChunkExpandingIndexMap:
496 verboseStr = "'expanding' index map";
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", *pAux,
504 *pAux >> 24, (char)(*pAux >> 16), (char)(*pAux >> 8), (char)*pAux,
505 verboseStr, size);
506
507 size = (size + 8 + 7) & ~7;
508 pAux += size / sizeof(u4);
509 }
510 printf("\n");
511}
512
513/*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800514 * Dump a class_def_item.
515 */
516void 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/*
Andy McFaddena2ee53b2009-05-05 16:52:10 -0700555 * Dump an interface that a class declares to implement.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800556 */
557void dumpInterface(const DexFile* pDexFile, const DexTypeItem* pTypeItem,
558 int i)
559{
560 const char* interfaceName =
561 dexStringByTypeIdx(pDexFile, pTypeItem->typeIdx);
562
Andy McFaddena2ee53b2009-05-05 16:52:10 -0700563 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 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800570}
571
572/*
573 * Dump the catches table associated with the code.
574 */
575void 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
616static 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 */
625void 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
638static 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 */
650void 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 */
667bool 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 */
686bool 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/*
703 * Look up a class' descriptor.
704 */
705const char* getClassDescriptor(DexFile* pDexFile, u4 classIdx)
706{
707 return dexStringByTypeIdx(pDexFile, classIdx);
708}
709
710/*
711 * Dump a single instruction.
712 */
713void dumpInstruction(DexFile* pDexFile, const DexCode* pCode, int insnIdx,
714 int insnWidth, const DecodedInstruction* pDecInsn)
715{
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800716 const u2* insns = pCode->insns;
717 int i;
718
719 printf("%06x:", ((u1*)insns - pDexFile->baseAddr) + insnIdx*2);
720 for (i = 0; i < 8; i++) {
721 if (i < insnWidth) {
722 if (i == 7) {
723 printf(" ... ");
724 } else {
725 /* print 16-bit value in little-endian order */
726 const u1* bytePtr = (const u1*) &insns[insnIdx+i];
727 printf(" %02x%02x", bytePtr[0], bytePtr[1]);
728 }
729 } else {
730 fputs(" ", stdout);
731 }
732 }
733
734 if (pDecInsn->opCode == OP_NOP) {
735 u2 instr = get2LE((const u1*) &insns[insnIdx]);
736 if (instr == kPackedSwitchSignature) {
737 printf("|%04x: packed-switch-data (%d units)",
738 insnIdx, insnWidth);
739 } else if (instr == kSparseSwitchSignature) {
740 printf("|%04x: sparse-switch-data (%d units)",
741 insnIdx, insnWidth);
742 } else if (instr == kArrayDataSignature) {
743 printf("|%04x: array-data (%d units)",
744 insnIdx, insnWidth);
745 } else {
746 printf("|%04x: nop // spacer", insnIdx);
747 }
748 } else {
749 printf("|%04x: %s", insnIdx, getOpcodeName(pDecInsn->opCode));
750 }
751
752 switch (dexGetInstrFormat(gInstrFormat, pDecInsn->opCode)) {
753 case kFmt10x: // op
754 break;
755 case kFmt12x: // op vA, vB
756 printf(" v%d, v%d", pDecInsn->vA, pDecInsn->vB);
757 break;
758 case kFmt11n: // op vA, #+B
759 printf(" v%d, #int %d // #%x",
760 pDecInsn->vA, (s4)pDecInsn->vB, (u1)pDecInsn->vB);
761 break;
762 case kFmt11x: // op vAA
763 printf(" v%d", pDecInsn->vA);
764 break;
765 case kFmt10t: // op +AA
766 case kFmt20t: // op +AAAA
767 {
768 s4 targ = (s4) pDecInsn->vA;
769 printf(" %04x // %c%04x",
770 insnIdx + targ,
771 (targ < 0) ? '-' : '+',
772 (targ < 0) ? -targ : targ);
773 }
774 break;
775 case kFmt22x: // op vAA, vBBBB
776 printf(" v%d, v%d", pDecInsn->vA, pDecInsn->vB);
777 break;
778 case kFmt21t: // op vAA, +BBBB
779 {
780 s4 targ = (s4) pDecInsn->vB;
781 printf(" v%d, %04x // %c%04x", pDecInsn->vA,
782 insnIdx + targ,
783 (targ < 0) ? '-' : '+',
784 (targ < 0) ? -targ : targ);
785 }
786 break;
787 case kFmt21s: // op vAA, #+BBBB
788 printf(" v%d, #int %d // #%x",
789 pDecInsn->vA, (s4)pDecInsn->vB, (u2)pDecInsn->vB);
790 break;
791 case kFmt21h: // op vAA, #+BBBB0000[00000000]
792 // The printed format varies a bit based on the actual opcode.
793 if (pDecInsn->opCode == OP_CONST_HIGH16) {
794 s4 value = pDecInsn->vB << 16;
795 printf(" v%d, #int %d // #%x",
796 pDecInsn->vA, value, (u2)pDecInsn->vB);
797 } else {
798 s8 value = ((s8) pDecInsn->vB) << 48;
799 printf(" v%d, #long %lld // #%x",
800 pDecInsn->vA, value, (u2)pDecInsn->vB);
801 }
802 break;
803 case kFmt21c: // op vAA, thing@BBBB
804 if (pDecInsn->opCode == OP_CONST_STRING) {
805 printf(" v%d, \"%s\" // string@%04x", pDecInsn->vA,
806 dexStringById(pDexFile, pDecInsn->vB), pDecInsn->vB);
807 } else if (pDecInsn->opCode == OP_CHECK_CAST ||
808 pDecInsn->opCode == OP_NEW_INSTANCE ||
809 pDecInsn->opCode == OP_CONST_CLASS)
810 {
811 printf(" v%d, %s // class@%04x", pDecInsn->vA,
812 getClassDescriptor(pDexFile, pDecInsn->vB), pDecInsn->vB);
813 } else /* OP_SGET* */ {
814 FieldMethodInfo fieldInfo;
815 if (getFieldInfo(pDexFile, pDecInsn->vB, &fieldInfo)) {
816 printf(" v%d, %s.%s:%s // field@%04x", pDecInsn->vA,
817 fieldInfo.classDescriptor, fieldInfo.name,
818 fieldInfo.signature, pDecInsn->vB);
819 } else {
820 printf(" v%d, ??? // field@%04x", pDecInsn->vA, pDecInsn->vB);
821 }
822 }
823 break;
824 case kFmt23x: // op vAA, vBB, vCC
825 printf(" v%d, v%d, v%d", pDecInsn->vA, pDecInsn->vB, pDecInsn->vC);
826 break;
827 case kFmt22b: // op vAA, vBB, #+CC
828 printf(" v%d, v%d, #int %d // #%02x",
829 pDecInsn->vA, pDecInsn->vB, (s4)pDecInsn->vC, (u1)pDecInsn->vC);
830 break;
831 case kFmt22t: // op vA, vB, +CCCC
832 {
833 s4 targ = (s4) pDecInsn->vC;
834 printf(" v%d, v%d, %04x // %c%04x", pDecInsn->vA, pDecInsn->vB,
835 insnIdx + targ,
836 (targ < 0) ? '-' : '+',
837 (targ < 0) ? -targ : targ);
838 }
839 break;
840 case kFmt22s: // op vA, vB, #+CCCC
841 printf(" v%d, v%d, #int %d // #%04x",
842 pDecInsn->vA, pDecInsn->vB, (s4)pDecInsn->vC, (u2)pDecInsn->vC);
843 break;
844 case kFmt22c: // op vA, vB, thing@CCCC
845 if (pDecInsn->opCode >= OP_IGET && pDecInsn->opCode <= OP_IPUT_SHORT) {
846 FieldMethodInfo fieldInfo;
847 if (getFieldInfo(pDexFile, pDecInsn->vC, &fieldInfo)) {
848 printf(" v%d, v%d, %s.%s:%s // field@%04x", pDecInsn->vA,
849 pDecInsn->vB, fieldInfo.classDescriptor, fieldInfo.name,
850 fieldInfo.signature, pDecInsn->vC);
851 } else {
852 printf(" v%d, v%d, ??? // field@%04x", pDecInsn->vA,
853 pDecInsn->vB, pDecInsn->vC);
854 }
855 } else {
856 printf(" v%d, v%d, %s // class@%04x",
857 pDecInsn->vA, pDecInsn->vB,
858 getClassDescriptor(pDexFile, pDecInsn->vC), pDecInsn->vC);
859 }
860 break;
861 case kFmt22cs: // [opt] op vA, vB, field offset CCCC
862 printf(" v%d, v%d, [obj+%04x]",
863 pDecInsn->vA, pDecInsn->vB, pDecInsn->vC);
864 break;
865 case kFmt30t:
866 printf(" #%08x", pDecInsn->vA);
867 break;
868 case kFmt31i: // op vAA, #+BBBBBBBB
869 {
870 /* this is often, but not always, a float */
871 union {
872 float f;
873 u4 i;
874 } conv;
875 conv.i = pDecInsn->vB;
876 printf(" v%d, #float %f // #%08x",
877 pDecInsn->vA, conv.f, pDecInsn->vB);
878 }
879 break;
880 case kFmt31c: // op vAA, thing@BBBBBBBB
881 printf(" v%d, \"%s\" // string@%08x", pDecInsn->vA,
882 dexStringById(pDexFile, pDecInsn->vB), pDecInsn->vB);
883 break;
884 case kFmt31t: // op vAA, offset +BBBBBBBB
885 printf(" v%d, %08x // +%08x",
886 pDecInsn->vA, insnIdx + pDecInsn->vB, pDecInsn->vB);
887 break;
888 case kFmt32x: // op vAAAA, vBBBB
889 printf(" v%d, v%d", pDecInsn->vA, pDecInsn->vB);
890 break;
891 case kFmt35c: // op vB, {vD, vE, vF, vG, vA}, thing@CCCC
892 {
893 /* NOTE: decoding of 35c doesn't quite match spec */
894 fputs(" {", stdout);
895 for (i = 0; i < (int) pDecInsn->vA; i++) {
896 if (i == 0)
897 printf("v%d", pDecInsn->arg[i]);
898 else
899 printf(", v%d", pDecInsn->arg[i]);
900 }
901 if (pDecInsn->opCode == OP_FILLED_NEW_ARRAY) {
902 printf("}, %s // class@%04x",
903 getClassDescriptor(pDexFile, pDecInsn->vB), pDecInsn->vB);
904 } else {
905 FieldMethodInfo methInfo;
906 if (getMethodInfo(pDexFile, pDecInsn->vB, &methInfo)) {
907 printf("}, %s.%s:%s // method@%04x",
908 methInfo.classDescriptor, methInfo.name,
909 methInfo.signature, pDecInsn->vB);
910 } else {
911 printf("}, ??? // method@%04x", pDecInsn->vB);
912 }
913 }
914 }
915 break;
916 case kFmt35ms: // [opt] invoke-virtual+super
917 case kFmt35fs: // [opt] invoke-interface
918 {
919 fputs(" {", stdout);
920 for (i = 0; i < (int) pDecInsn->vA; i++) {
921 if (i == 0)
922 printf("v%d", pDecInsn->arg[i]);
923 else
924 printf(", v%d", pDecInsn->arg[i]);
925 }
926 printf("}, [%04x] // vtable #%04x", pDecInsn->vB, pDecInsn->vB);
927 }
928 break;
929 case kFmt3rc: // op {vCCCC .. v(CCCC+AA-1)}, meth@BBBB
930 {
931 /*
932 * This doesn't match the "dx" output when some of the args are
933 * 64-bit values -- dx only shows the first register.
934 */
935 fputs(" {", stdout);
936 for (i = 0; i < (int) pDecInsn->vA; i++) {
937 if (i == 0)
938 printf("v%d", pDecInsn->vC + i);
939 else
940 printf(", v%d", pDecInsn->vC + i);
941 }
942 if (pDecInsn->opCode == OP_FILLED_NEW_ARRAY_RANGE) {
943 printf("}, %s // class@%04x",
944 getClassDescriptor(pDexFile, pDecInsn->vB), pDecInsn->vB);
945 } else {
946 FieldMethodInfo methInfo;
947 if (getMethodInfo(pDexFile, pDecInsn->vB, &methInfo)) {
948 printf("}, %s.%s:%s // method@%04x",
949 methInfo.classDescriptor, methInfo.name,
950 methInfo.signature, pDecInsn->vB);
951 } else {
952 printf("}, ??? // method@%04x", pDecInsn->vB);
953 }
954 }
955 }
956 break;
957 case kFmt3rms: // [opt] invoke-virtual+super/range
958 case kFmt3rfs: // [opt] invoke-interface/range
959 {
960 /*
961 * This doesn't match the "dx" output when some of the args are
962 * 64-bit values -- dx only shows the first register.
963 */
964 fputs(" {", stdout);
965 for (i = 0; i < (int) pDecInsn->vA; i++) {
966 if (i == 0)
967 printf("v%d", pDecInsn->vC + i);
968 else
969 printf(", v%d", pDecInsn->vC + i);
970 }
971 printf("}, [%04x] // vtable #%04x", pDecInsn->vB, pDecInsn->vB);
972 }
973 break;
Andy McFaddenb0a05412009-11-19 10:23:41 -0800974 case kFmt3rinline: // [opt] execute-inline/range
975 {
976 fputs(" {", stdout);
977 for (i = 0; i < (int) pDecInsn->vA; i++) {
978 if (i == 0)
979 printf("v%d", pDecInsn->vC + i);
980 else
981 printf(", v%d", pDecInsn->vC + i);
982 }
983 printf("}, [%04x] // inline #%04x", pDecInsn->vB, pDecInsn->vB);
984 }
985 break;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800986 case kFmt3inline: // [opt] inline invoke
987 {
988#if 0
989 const InlineOperation* inlineOpsTable = dvmGetInlineOpsTable();
990 u4 tableLen = dvmGetInlineOpsTableLength();
991#endif
992
993 fputs(" {", stdout);
994 for (i = 0; i < (int) pDecInsn->vA; i++) {
995 if (i == 0)
996 printf("v%d", pDecInsn->arg[i]);
997 else
998 printf(", v%d", pDecInsn->arg[i]);
999 }
1000#if 0
1001 if (pDecInsn->vB < tableLen) {
1002 printf("}, %s.%s:%s // inline #%04x",
1003 inlineOpsTable[pDecInsn->vB].classDescriptor,
1004 inlineOpsTable[pDecInsn->vB].methodName,
1005 inlineOpsTable[pDecInsn->vB].methodSignature,
1006 pDecInsn->vB);
1007 } else {
1008#endif
1009 printf("}, [%04x] // inline #%04x", pDecInsn->vB, pDecInsn->vB);
1010#if 0
1011 }
1012#endif
1013 }
1014 break;
1015 case kFmt51l: // op vAA, #+BBBBBBBBBBBBBBBB
1016 {
1017 /* this is often, but not always, a double */
1018 union {
1019 double d;
1020 u8 j;
1021 } conv;
1022 conv.j = pDecInsn->vB_wide;
1023 printf(" v%d, #double %f // #%016llx",
1024 pDecInsn->vA, conv.d, pDecInsn->vB_wide);
1025 }
1026 break;
1027 case kFmtUnknown:
1028 break;
1029 default:
1030 printf(" ???");
1031 break;
1032 }
1033
1034
1035 putchar('\n');
1036
1037}
1038
1039/*
1040 * Dump a bytecode disassembly.
1041 */
1042void dumpBytecodes(DexFile* pDexFile, const DexMethod* pDexMethod)
1043{
1044 const DexCode* pCode = dexGetCode(pDexFile, pDexMethod);
1045 const u2* insns;
1046 int insnIdx;
1047 FieldMethodInfo methInfo;
1048 int startAddr;
1049 char* className = NULL;
1050
1051 assert(pCode->insnsSize > 0);
1052 insns = pCode->insns;
1053
1054 getMethodInfo(pDexFile, pDexMethod->methodIdx, &methInfo);
1055 startAddr = ((u1*)pCode - pDexFile->baseAddr);
1056 className = descriptorToDot(methInfo.classDescriptor);
1057
1058 printf("%06x: |[%06x] %s.%s:%s\n",
1059 startAddr, startAddr,
1060 className, methInfo.name, methInfo.signature);
1061
1062 insnIdx = 0;
1063 while (insnIdx < (int) pCode->insnsSize) {
1064 int insnWidth;
1065 OpCode opCode;
1066 DecodedInstruction decInsn;
1067 u2 instr;
1068
1069 instr = get2LE((const u1*)insns);
1070 if (instr == kPackedSwitchSignature) {
1071 insnWidth = 4 + get2LE((const u1*)(insns+1)) * 2;
1072 } else if (instr == kSparseSwitchSignature) {
1073 insnWidth = 2 + get2LE((const u1*)(insns+1)) * 4;
1074 } else if (instr == kArrayDataSignature) {
1075 int width = get2LE((const u1*)(insns+1));
1076 int size = get2LE((const u1*)(insns+2)) |
1077 (get2LE((const u1*)(insns+3))<<16);
1078 // The plus 1 is to round up for odd size and width
1079 insnWidth = 4 + ((size * width) + 1) / 2;
1080 } else {
1081 opCode = instr & 0xff;
1082 insnWidth = dexGetInstrWidthAbs(gInstrWidth, opCode);
1083 if (insnWidth == 0) {
1084 fprintf(stderr,
1085 "GLITCH: zero-width instruction at idx=0x%04x\n", insnIdx);
1086 break;
1087 }
1088 }
1089
1090 dexDecodeInstruction(gInstrFormat, insns, &decInsn);
1091 dumpInstruction(pDexFile, pCode, insnIdx, insnWidth, &decInsn);
1092
1093 insns += insnWidth;
1094 insnIdx += insnWidth;
1095 }
1096
1097 free(className);
1098}
1099
1100/*
1101 * Dump a "code" struct.
1102 */
1103void dumpCode(DexFile* pDexFile, const DexMethod* pDexMethod)
1104{
1105 const DexCode* pCode = dexGetCode(pDexFile, pDexMethod);
1106
1107 printf(" registers : %d\n", pCode->registersSize);
1108 printf(" ins : %d\n", pCode->insSize);
1109 printf(" outs : %d\n", pCode->outsSize);
1110 printf(" insns size : %d 16-bit code units\n", pCode->insnsSize);
1111
1112 if (gOptions.disassemble)
1113 dumpBytecodes(pDexFile, pDexMethod);
1114
1115 dumpCatches(pDexFile, pCode);
1116 /* both of these are encoded in debug info */
1117 dumpPositions(pDexFile, pCode, pDexMethod);
1118 dumpLocals(pDexFile, pCode, pDexMethod);
1119}
1120
1121/*
1122 * Dump a method.
1123 */
1124void dumpMethod(DexFile* pDexFile, const DexMethod* pDexMethod, int i)
1125{
1126 const DexMethodId* pMethodId;
1127 const char* backDescriptor;
1128 const char* name;
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001129 char* typeDescriptor = NULL;
1130 char* accessStr = NULL;
1131
1132 if (gOptions.exportsOnly &&
1133 (pDexMethod->accessFlags & (ACC_PUBLIC | ACC_PROTECTED)) == 0)
1134 {
1135 return;
1136 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001137
1138 pMethodId = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
1139 name = dexStringById(pDexFile, pMethodId->nameIdx);
1140 typeDescriptor = dexCopyDescriptorFromMethodId(pDexFile, pMethodId);
1141
1142 backDescriptor = dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
1143
1144 accessStr = createAccessFlagStr(pDexMethod->accessFlags,
1145 kAccessForMethod);
1146
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001147 if (gOptions.outputFormat == OUTPUT_PLAIN) {
1148 printf(" #%d : (in %s)\n", i, backDescriptor);
1149 printf(" name : '%s'\n", name);
1150 printf(" type : '%s'\n", typeDescriptor);
1151 printf(" access : 0x%04x (%s)\n",
1152 pDexMethod->accessFlags, accessStr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001153
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001154 if (pDexMethod->codeOff == 0) {
1155 printf(" code : (none)\n");
1156 } else {
1157 printf(" code -\n");
1158 dumpCode(pDexFile, pDexMethod);
1159 }
1160
1161 if (gOptions.disassemble)
1162 putchar('\n');
1163 } else if (gOptions.outputFormat == OUTPUT_XML) {
1164 bool constructor = (name[0] == '<');
1165
1166 if (constructor) {
1167 char* tmp;
1168
1169 tmp = descriptorClassToDot(backDescriptor);
1170 printf("<constructor name=\"%s\"\n", tmp);
1171 free(tmp);
1172
1173 tmp = descriptorToDot(backDescriptor);
1174 printf(" type=\"%s\"\n", tmp);
1175 free(tmp);
1176 } else {
1177 printf("<method name=\"%s\"\n", name);
1178
1179 const char* returnType = strrchr(typeDescriptor, ')');
1180 if (returnType == NULL) {
1181 fprintf(stderr, "bad method type descriptor '%s'\n",
1182 typeDescriptor);
1183 goto bail;
1184 }
1185
1186 char* tmp = descriptorToDot(returnType+1);
1187 printf(" return=\"%s\"\n", tmp);
1188 free(tmp);
1189
1190 printf(" abstract=%s\n",
1191 quotedBool((pDexMethod->accessFlags & ACC_ABSTRACT) != 0));
1192 printf(" native=%s\n",
1193 quotedBool((pDexMethod->accessFlags & ACC_NATIVE) != 0));
1194
1195 bool isSync =
1196 (pDexMethod->accessFlags & ACC_SYNCHRONIZED) != 0 ||
1197 (pDexMethod->accessFlags & ACC_DECLARED_SYNCHRONIZED) != 0;
1198 printf(" synchronized=%s\n", quotedBool(isSync));
1199 }
1200
1201 printf(" static=%s\n",
1202 quotedBool((pDexMethod->accessFlags & ACC_STATIC) != 0));
1203 printf(" final=%s\n",
1204 quotedBool((pDexMethod->accessFlags & ACC_FINAL) != 0));
1205 // "deprecated=" not knowable w/o parsing annotations
1206 printf(" visibility=%s\n",
1207 quotedVisibility(pDexMethod->accessFlags));
1208
1209 printf(">\n");
1210
1211 /*
1212 * Parameters.
1213 */
1214 if (typeDescriptor[0] != '(') {
1215 fprintf(stderr, "ERROR: bad descriptor '%s'\n", typeDescriptor);
1216 goto bail;
1217 }
1218
1219 char tmpBuf[strlen(typeDescriptor)+1]; /* more than big enough */
1220 int argNum = 0;
1221
1222 const char* base = typeDescriptor+1;
1223
1224 while (*base != ')') {
1225 char* cp = tmpBuf;
1226
1227 while (*base == '[')
1228 *cp++ = *base++;
1229
1230 if (*base == 'L') {
1231 /* copy through ';' */
1232 do {
1233 *cp = *base++;
1234 } while (*cp++ != ';');
1235 } else {
1236 /* primitive char, copy it */
1237 if (strchr("ZBCSIFJD", *base) == NULL) {
1238 fprintf(stderr, "ERROR: bad method signature '%s'\n", base);
1239 goto bail;
1240 }
1241 *cp++ = *base++;
1242 }
1243
1244 /* null terminate and display */
1245 *cp++ = '\0';
1246
1247 char* tmp = descriptorToDot(tmpBuf);
1248 printf("<parameter name=\"arg%d\" type=\"%s\">\n</parameter>\n",
1249 argNum++, tmp);
1250 free(tmp);
1251 }
1252
1253 if (constructor)
1254 printf("</constructor>\n");
1255 else
1256 printf("</method>\n");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001257 }
1258
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001259bail:
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001260 free(typeDescriptor);
1261 free(accessStr);
1262}
1263
1264/*
1265 * Dump a static (class) field.
1266 */
1267void dumpSField(const DexFile* pDexFile, const DexField* pSField, int i)
1268{
1269 const DexFieldId* pFieldId;
1270 const char* backDescriptor;
1271 const char* name;
1272 const char* typeDescriptor;
1273 char* accessStr;
1274
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001275 if (gOptions.exportsOnly &&
1276 (pSField->accessFlags & (ACC_PUBLIC | ACC_PROTECTED)) == 0)
1277 {
1278 return;
1279 }
1280
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001281 pFieldId = dexGetFieldId(pDexFile, pSField->fieldIdx);
1282 name = dexStringById(pDexFile, pFieldId->nameIdx);
1283 typeDescriptor = dexStringByTypeIdx(pDexFile, pFieldId->typeIdx);
1284 backDescriptor = dexStringByTypeIdx(pDexFile, pFieldId->classIdx);
1285
1286 accessStr = createAccessFlagStr(pSField->accessFlags, kAccessForField);
1287
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001288 if (gOptions.outputFormat == OUTPUT_PLAIN) {
1289 printf(" #%d : (in %s)\n", i, backDescriptor);
1290 printf(" name : '%s'\n", name);
1291 printf(" type : '%s'\n", typeDescriptor);
1292 printf(" access : 0x%04x (%s)\n",
1293 pSField->accessFlags, accessStr);
1294 } else if (gOptions.outputFormat == OUTPUT_XML) {
1295 char* tmp;
1296
1297 printf("<field name=\"%s\"\n", name);
1298
1299 tmp = descriptorToDot(typeDescriptor);
1300 printf(" type=\"%s\"\n", tmp);
1301 free(tmp);
1302
1303 printf(" transient=%s\n",
1304 quotedBool((pSField->accessFlags & ACC_TRANSIENT) != 0));
1305 printf(" volatile=%s\n",
1306 quotedBool((pSField->accessFlags & ACC_VOLATILE) != 0));
1307 // "value=" not knowable w/o parsing annotations
1308 printf(" static=%s\n",
1309 quotedBool((pSField->accessFlags & ACC_STATIC) != 0));
1310 printf(" final=%s\n",
1311 quotedBool((pSField->accessFlags & ACC_FINAL) != 0));
1312 // "deprecated=" not knowable w/o parsing annotations
1313 printf(" visibility=%s\n",
1314 quotedVisibility(pSField->accessFlags));
1315 printf(">\n</field>\n");
1316 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001317
1318 free(accessStr);
1319}
1320
1321/*
1322 * Dump an instance field.
1323 */
1324void dumpIField(const DexFile* pDexFile, const DexField* pIField, int i)
1325{
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001326 dumpSField(pDexFile, pIField, i);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001327}
1328
1329/*
1330 * Dump the class.
The Android Open Source Project99409882009-03-18 22:20:24 -07001331 *
1332 * Note "idx" is a DexClassDef index, not a DexTypeId index.
Andy McFaddend18aff32009-05-06 10:19:16 -07001333 *
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001334 * If "*pLastPackage" is NULL or does not match the current class' package,
1335 * the value will be replaced with a newly-allocated string.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001336 */
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001337void dumpClass(DexFile* pDexFile, int idx, char** pLastPackage)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001338{
1339 const DexTypeList* pInterfaces;
1340 const DexClassDef* pClassDef;
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001341 DexClassData* pClassData = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001342 const u1* pEncodedData;
1343 const char* fileName;
1344 const char* classDescriptor;
1345 const char* superclassDescriptor;
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001346 char* accessStr = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001347 int i;
1348
1349 pClassDef = dexGetClassDef(pDexFile, idx);
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001350
1351 if (gOptions.exportsOnly && (pClassDef->accessFlags & ACC_PUBLIC) == 0) {
1352 //printf("<!-- omitting non-public class %s -->\n",
1353 // classDescriptor);
1354 goto bail;
1355 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001356
1357 pEncodedData = dexGetClassData(pDexFile, pClassDef);
1358 pClassData = dexReadAndVerifyClassData(&pEncodedData, NULL);
1359
1360 if (pClassData == NULL) {
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001361 printf("Trouble reading class data (#%d)\n", idx);
1362 goto bail;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001363 }
1364
1365 classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001366
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001367 /*
1368 * For the XML output, show the package name. Ideally we'd gather
1369 * up the classes, sort them, and dump them alphabetically so the
1370 * package name wouldn't jump around, but that's not a great plan
1371 * for something that needs to run on the device.
1372 */
1373 if (!(classDescriptor[0] == 'L' &&
1374 classDescriptor[strlen(classDescriptor)-1] == ';'))
1375 {
1376 /* arrays and primitives should not be defined explicitly */
1377 fprintf(stderr, "Malformed class name '%s'\n", classDescriptor);
1378 /* keep going? */
1379 } else if (gOptions.outputFormat == OUTPUT_XML) {
1380 char* mangle;
1381 char* lastSlash;
1382 char* cp;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001383
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001384 mangle = strdup(classDescriptor + 1);
1385 mangle[strlen(mangle)-1] = '\0';
1386
1387 /* reduce to just the package name */
1388 lastSlash = strrchr(mangle, '/');
1389 if (lastSlash != NULL) {
1390 *lastSlash = '\0';
1391 } else {
1392 *mangle = '\0';
1393 }
1394
1395 for (cp = mangle; *cp != '\0'; cp++) {
1396 if (*cp == '/')
1397 *cp = '.';
1398 }
1399
1400 if (*pLastPackage == NULL || strcmp(mangle, *pLastPackage) != 0) {
1401 /* start of a new package */
1402 if (*pLastPackage != NULL)
1403 printf("</package>\n");
1404 printf("<package name=\"%s\"\n>\n", mangle);
1405 free(*pLastPackage);
1406 *pLastPackage = mangle;
1407 } else {
1408 free(mangle);
1409 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001410 }
1411
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001412 accessStr = createAccessFlagStr(pClassDef->accessFlags, kAccessForClass);
1413
1414 if (pClassDef->superclassIdx == kDexNoIndex) {
1415 superclassDescriptor = NULL;
1416 } else {
1417 superclassDescriptor =
1418 dexStringByTypeIdx(pDexFile, pClassDef->superclassIdx);
1419 }
1420
1421 if (gOptions.outputFormat == OUTPUT_PLAIN) {
1422 printf("Class #%d -\n", idx);
1423 printf(" Class descriptor : '%s'\n", classDescriptor);
1424 printf(" Access flags : 0x%04x (%s)\n",
1425 pClassDef->accessFlags, accessStr);
1426
1427 if (superclassDescriptor != NULL)
1428 printf(" Superclass : '%s'\n", superclassDescriptor);
1429
1430 printf(" Interfaces -\n");
1431 } else {
1432 char* tmp;
1433
1434 tmp = descriptorClassToDot(classDescriptor);
1435 printf("<class name=\"%s\"\n", tmp);
1436 free(tmp);
1437
1438 if (superclassDescriptor != NULL) {
1439 tmp = descriptorToDot(superclassDescriptor);
1440 printf(" extends=\"%s\"\n", tmp);
1441 free(tmp);
1442 }
1443 printf(" abstract=%s\n",
1444 quotedBool((pClassDef->accessFlags & ACC_ABSTRACT) != 0));
1445 printf(" static=%s\n",
1446 quotedBool((pClassDef->accessFlags & ACC_STATIC) != 0));
1447 printf(" final=%s\n",
1448 quotedBool((pClassDef->accessFlags & ACC_FINAL) != 0));
1449 // "deprecated=" not knowable w/o parsing annotations
1450 printf(" visibility=%s\n",
1451 quotedVisibility(pClassDef->accessFlags));
1452 printf(">\n");
1453 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001454 pInterfaces = dexGetInterfacesList(pDexFile, pClassDef);
1455 if (pInterfaces != NULL) {
1456 for (i = 0; i < (int) pInterfaces->size; i++)
1457 dumpInterface(pDexFile, dexGetTypeItem(pInterfaces, i), i);
1458 }
1459
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001460 if (gOptions.outputFormat == OUTPUT_PLAIN)
1461 printf(" Static fields -\n");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001462 for (i = 0; i < (int) pClassData->header.staticFieldsSize; i++) {
1463 dumpSField(pDexFile, &pClassData->staticFields[i], i);
1464 }
1465
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001466 if (gOptions.outputFormat == OUTPUT_PLAIN)
1467 printf(" Instance fields -\n");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001468 for (i = 0; i < (int) pClassData->header.instanceFieldsSize; i++) {
1469 dumpIField(pDexFile, &pClassData->instanceFields[i], i);
1470 }
1471
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001472 if (gOptions.outputFormat == OUTPUT_PLAIN)
1473 printf(" Direct methods -\n");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001474 for (i = 0; i < (int) pClassData->header.directMethodsSize; i++) {
1475 dumpMethod(pDexFile, &pClassData->directMethods[i], i);
1476 }
1477
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001478 if (gOptions.outputFormat == OUTPUT_PLAIN)
1479 printf(" Virtual methods -\n");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001480 for (i = 0; i < (int) pClassData->header.virtualMethodsSize; i++) {
1481 dumpMethod(pDexFile, &pClassData->virtualMethods[i], i);
1482 }
1483
1484 // TODO: Annotations.
1485
1486 if (pClassDef->sourceFileIdx != kDexNoIndex)
1487 fileName = dexStringById(pDexFile, pClassDef->sourceFileIdx);
1488 else
1489 fileName = "unknown";
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001490
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001491 if (gOptions.outputFormat == OUTPUT_PLAIN) {
1492 printf(" source_file_idx : %d (%s)\n",
1493 pClassDef->sourceFileIdx, fileName);
1494 printf("\n");
1495 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001496
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001497 if (gOptions.outputFormat == OUTPUT_XML) {
1498 printf("</class>\n");
1499 }
1500
1501bail:
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001502 free(pClassData);
1503 free(accessStr);
1504}
1505
The Android Open Source Project99409882009-03-18 22:20:24 -07001506
1507/*
1508 * Advance "ptr" to ensure 32-bit alignment.
1509 */
1510static inline const u1* align32(const u1* ptr)
1511{
1512 return (u1*) (((int) ptr + 3) & ~0x03);
1513}
1514
Andy McFadden10351272009-03-24 21:30:32 -07001515
1516/*
1517 * Dump a map in the "differential" format.
1518 *
1519 * TODO: show a hex dump of the compressed data. (We can show the
1520 * uncompressed data if we move the compression code to libdex; otherwise
1521 * it's too complex to merit a fast & fragile implementation here.)
1522 */
1523void dumpDifferentialCompressedMap(const u1** pData)
1524{
1525 const u1* data = *pData;
1526 const u1* dataStart = data -1; // format byte already removed
1527 u1 regWidth;
1528 u2 numEntries;
1529
1530 /* standard header */
1531 regWidth = *data++;
1532 numEntries = *data++;
1533 numEntries |= (*data++) << 8;
1534
1535 /* compressed data begins with the compressed data length */
1536 int compressedLen = readUnsignedLeb128(&data);
1537 int addrWidth = 1;
1538 if ((*data & 0x80) != 0)
1539 addrWidth++;
1540
1541 int origLen = 4 + (addrWidth + regWidth) * numEntries;
1542 int compLen = (data - dataStart) + compressedLen;
1543
1544 printf(" (differential compression %d -> %d [%d -> %d])\n",
1545 origLen, compLen,
1546 (addrWidth + regWidth) * numEntries, compressedLen);
1547
1548 /* skip past end of entry */
1549 data += compressedLen;
1550
1551 *pData = data;
1552}
1553
The Android Open Source Project99409882009-03-18 22:20:24 -07001554/*
1555 * Dump register map contents of the current method.
1556 *
1557 * "*pData" should point to the start of the register map data. Advances
1558 * "*pData" to the start of the next map.
1559 */
1560void dumpMethodMap(DexFile* pDexFile, const DexMethod* pDexMethod, int idx,
1561 const u1** pData)
1562{
1563 const u1* data = *pData;
1564 const DexMethodId* pMethodId;
1565 const char* name;
1566 int offset = data - (u1*) pDexFile->pOptHeader;
1567
1568 pMethodId = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
1569 name = dexStringById(pDexFile, pMethodId->nameIdx);
1570 printf(" #%d: 0x%08x %s\n", idx, offset, name);
1571
1572 u1 format;
1573 int addrWidth;
1574
1575 format = *data++;
1576 if (format == 1) { /* kRegMapFormatNone */
1577 /* no map */
1578 printf(" (no map)\n");
1579 addrWidth = 0;
1580 } else if (format == 2) { /* kRegMapFormatCompact8 */
1581 addrWidth = 1;
1582 } else if (format == 3) { /* kRegMapFormatCompact16 */
1583 addrWidth = 2;
Andy McFadden10351272009-03-24 21:30:32 -07001584 } else if (format == 4) { /* kRegMapFormatDifferential */
1585 dumpDifferentialCompressedMap(&data);
1586 goto bail;
The Android Open Source Project99409882009-03-18 22:20:24 -07001587 } else {
1588 printf(" (unknown format %d!)\n", format);
Andy McFadden10351272009-03-24 21:30:32 -07001589 /* don't know how to skip data; failure will cascade to end of class */
1590 goto bail;
The Android Open Source Project99409882009-03-18 22:20:24 -07001591 }
1592
1593 if (addrWidth > 0) {
1594 u1 regWidth;
1595 u2 numEntries;
1596 int idx, addr, byte;
1597
1598 regWidth = *data++;
1599 numEntries = *data++;
1600 numEntries |= (*data++) << 8;
1601
1602 for (idx = 0; idx < numEntries; idx++) {
1603 addr = *data++;
1604 if (addrWidth > 1)
1605 addr |= (*data++) << 8;
1606
1607 printf(" %4x:", addr);
1608 for (byte = 0; byte < regWidth; byte++) {
1609 printf(" %02x", *data++);
1610 }
1611 printf("\n");
1612 }
1613 }
1614
Andy McFadden10351272009-03-24 21:30:32 -07001615bail:
The Android Open Source Project99409882009-03-18 22:20:24 -07001616 //if (addrWidth >= 0)
1617 // *pData = align32(data);
1618 *pData = data;
1619}
1620
1621/*
1622 * Dump the contents of the register map area.
1623 *
1624 * These are only present in optimized DEX files, and the structure is
1625 * not really exposed to other parts of the VM itself. We're going to
1626 * dig through them here, but this is pretty fragile. DO NOT rely on
1627 * this or derive other code from it.
1628 */
1629void dumpRegisterMaps(DexFile* pDexFile)
1630{
1631 const u1* pClassPool = pDexFile->pRegisterMapPool;
1632 const u4* classOffsets;
1633 const u1* ptr;
1634 u4 numClasses;
1635 int baseFileOffset = (u1*) pClassPool - (u1*) pDexFile->pOptHeader;
1636 int idx;
1637
1638 if (pClassPool == NULL) {
1639 printf("No register maps found\n");
1640 return;
1641 }
1642
1643 ptr = pClassPool;
1644 numClasses = get4LE(ptr);
1645 ptr += sizeof(u4);
1646 classOffsets = (const u4*) ptr;
1647
1648 printf("RMAP begins at offset 0x%07x\n", baseFileOffset);
1649 printf("Maps for %d classes\n", numClasses);
1650 for (idx = 0; idx < (int) numClasses; idx++) {
1651 const DexClassDef* pClassDef;
1652 const char* classDescriptor;
1653
1654 pClassDef = dexGetClassDef(pDexFile, idx);
1655 classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
1656
1657 printf("%4d: +%d (0x%08x) %s\n", idx, classOffsets[idx],
1658 baseFileOffset + classOffsets[idx], classDescriptor);
1659
1660 if (classOffsets[idx] == 0)
1661 continue;
1662
1663 /*
1664 * What follows is a series of RegisterMap entries, one for every
1665 * direct method, then one for every virtual method.
1666 */
1667 DexClassData* pClassData;
1668 const u1* pEncodedData;
1669 const u1* data = (u1*) pClassPool + classOffsets[idx];
1670 u2 methodCount;
1671 int i;
1672
1673 pEncodedData = dexGetClassData(pDexFile, pClassDef);
1674 pClassData = dexReadAndVerifyClassData(&pEncodedData, NULL);
1675 if (pClassData == NULL) {
1676 fprintf(stderr, "Trouble reading class data\n");
1677 continue;
1678 }
1679
1680 methodCount = *data++;
1681 methodCount |= (*data++) << 8;
1682 data += 2; /* two pad bytes follow methodCount */
1683 if (methodCount != pClassData->header.directMethodsSize
1684 + pClassData->header.virtualMethodsSize)
1685 {
1686 printf("NOTE: method count discrepancy (%d != %d + %d)\n",
1687 methodCount, pClassData->header.directMethodsSize,
1688 pClassData->header.virtualMethodsSize);
1689 /* this is bad, but keep going anyway */
1690 }
1691
1692 printf(" direct methods: %d\n",
1693 pClassData->header.directMethodsSize);
1694 for (i = 0; i < (int) pClassData->header.directMethodsSize; i++) {
1695 dumpMethodMap(pDexFile, &pClassData->directMethods[i], i, &data);
1696 }
1697
1698 printf(" virtual methods: %d\n",
1699 pClassData->header.virtualMethodsSize);
1700 for (i = 0; i < (int) pClassData->header.virtualMethodsSize; i++) {
1701 dumpMethodMap(pDexFile, &pClassData->virtualMethods[i], i, &data);
1702 }
1703
1704 free(pClassData);
1705 }
1706}
1707
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001708/*
1709 * Dump the requested sections of the file.
1710 */
1711void processDexFile(const char* fileName, DexFile* pDexFile)
1712{
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001713 char* package = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001714 int i;
1715
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001716 if (gOptions.verbose) {
1717 printf("Opened '%s', DEX version '%.3s'\n", fileName,
1718 pDexFile->pHeader->magic +4);
1719 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001720
The Android Open Source Project99409882009-03-18 22:20:24 -07001721 if (gOptions.dumpRegisterMaps) {
1722 dumpRegisterMaps(pDexFile);
1723 return;
1724 }
1725
Andy McFadden0ea77b92010-04-20 14:18:59 -07001726 if (gOptions.showFileHeaders) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001727 dumpFileHeader(pDexFile);
Andy McFadden0ea77b92010-04-20 14:18:59 -07001728 dumpAuxDirectory(pDexFile);
1729 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001730
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001731 if (gOptions.outputFormat == OUTPUT_XML)
1732 printf("<api>\n");
1733
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001734 for (i = 0; i < (int) pDexFile->pHeader->classDefsSize; i++) {
1735 if (gOptions.showSectionHeaders)
1736 dumpClassDef(pDexFile, i);
1737
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001738 dumpClass(pDexFile, i, &package);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001739 }
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001740
1741 /* free the last one allocated */
1742 if (package != NULL) {
1743 printf("</package>\n");
1744 free(package);
1745 }
1746
1747 if (gOptions.outputFormat == OUTPUT_XML)
1748 printf("</api>\n");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001749}
1750
1751
1752/*
1753 * Process one file.
1754 */
1755int process(const char* fileName)
1756{
1757 DexFile* pDexFile = NULL;
1758 MemMapping map;
1759 bool mapped = false;
1760 int result = -1;
1761
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001762 if (gOptions.verbose)
1763 printf("Processing '%s'...\n", fileName);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001764
1765 if (dexOpenAndMap(fileName, gOptions.tempFileName, &map, false) != 0)
1766 goto bail;
1767 mapped = true;
1768
Andy McFadden2124cb82009-03-25 15:37:39 -07001769 int flags = kDexParseVerifyChecksum;
1770 if (gOptions.ignoreBadChecksum)
1771 flags |= kDexParseContinueOnError;
1772
1773 pDexFile = dexFileParse(map.addr, map.length, flags);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001774 if (pDexFile == NULL) {
1775 fprintf(stderr, "ERROR: DEX parse failed\n");
1776 goto bail;
1777 }
1778
Andy McFadden0198b142009-04-02 14:48:56 -07001779 if (gOptions.checksumOnly) {
1780 printf("Checksum verified\n");
1781 } else {
1782 processDexFile(fileName, pDexFile);
1783 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001784
1785 result = 0;
1786
1787bail:
1788 if (mapped)
1789 sysReleaseShmem(&map);
1790 if (pDexFile != NULL)
1791 dexFileFree(pDexFile);
1792 return result;
1793}
1794
1795
1796/*
1797 * Show usage.
1798 */
1799void usage(void)
1800{
1801 fprintf(stderr, "Copyright (C) 2007 The Android Open Source Project\n\n");
Andy McFadden0198b142009-04-02 14:48:56 -07001802 fprintf(stderr,
Andy McFaddend18aff32009-05-06 10:19:16 -07001803 "%s: [-c] [-d] [-f] [-h] [-i] [-l layout] [-m] [-t tempfile] dexfile...\n",
The Android Open Source Project99409882009-03-18 22:20:24 -07001804 gProgName);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001805 fprintf(stderr, "\n");
Andy McFadden0198b142009-04-02 14:48:56 -07001806 fprintf(stderr, " -c : verify checksum and exit\n");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001807 fprintf(stderr, " -d : disassemble code sections\n");
1808 fprintf(stderr, " -f : display summary information from file header\n");
1809 fprintf(stderr, " -h : display file header details\n");
Andy McFadden2124cb82009-03-25 15:37:39 -07001810 fprintf(stderr, " -i : ignore checksum failures\n");
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001811 fprintf(stderr, " -l : output layout, either 'plain' or 'xml'\n");
The Android Open Source Project99409882009-03-18 22:20:24 -07001812 fprintf(stderr, " -m : dump register maps (and nothing else)\n");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001813 fprintf(stderr, " -t : temp file name (defaults to /sdcard/dex-temp-*)\n");
1814}
1815
1816/*
1817 * Parse args.
1818 *
1819 * I'm not using getopt_long() because we may not have it in libc.
1820 */
1821int main(int argc, char* const argv[])
1822{
1823 bool wantUsage = false;
1824 int ic;
1825
1826 memset(&gOptions, 0, sizeof(gOptions));
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001827 gOptions.verbose = true;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001828
1829 while (1) {
Andy McFaddend18aff32009-05-06 10:19:16 -07001830 ic = getopt(argc, argv, "cdfhil:mt:");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001831 if (ic < 0)
1832 break;
1833
1834 switch (ic) {
Andy McFadden0198b142009-04-02 14:48:56 -07001835 case 'c': // verify the checksum then exit
1836 gOptions.checksumOnly = true;
1837 break;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001838 case 'd': // disassemble Dalvik instructions
1839 gOptions.disassemble = true;
1840 break;
1841 case 'f': // dump outer file header
1842 gOptions.showFileHeaders = true;
1843 break;
1844 case 'h': // dump section headers, i.e. all meta-data
1845 gOptions.showSectionHeaders = true;
1846 break;
Andy McFadden2124cb82009-03-25 15:37:39 -07001847 case 'i': // continue even if checksum is bad
1848 gOptions.ignoreBadChecksum = true;
1849 break;
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001850 case 'l': // layout
1851 if (strcmp(optarg, "plain") == 0) {
1852 gOptions.outputFormat = OUTPUT_PLAIN;
1853 } else if (strcmp(optarg, "xml") == 0) {
1854 gOptions.outputFormat = OUTPUT_XML;
1855 gOptions.verbose = false;
1856 gOptions.exportsOnly = true;
1857 } else {
1858 wantUsage = true;
1859 }
1860 break;
The Android Open Source Project99409882009-03-18 22:20:24 -07001861 case 'm': // dump register maps only
1862 gOptions.dumpRegisterMaps = true;
1863 break;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001864 case 't': // temp file, used when opening compressed Jar
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001865 gOptions.tempFileName = optarg;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001866 break;
1867 default:
1868 wantUsage = true;
1869 break;
1870 }
1871 }
1872
1873 if (optind == argc) {
1874 fprintf(stderr, "%s: no file specified\n", gProgName);
1875 wantUsage = true;
1876 }
1877
Andy McFadden0198b142009-04-02 14:48:56 -07001878 if (gOptions.checksumOnly && gOptions.ignoreBadChecksum) {
1879 fprintf(stderr, "Can't specify both -c and -i\n");
1880 wantUsage = true;
1881 }
1882
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001883 /* initialize some VM tables */
1884 gInstrWidth = dexCreateInstrWidthTable();
1885 gInstrFormat = dexCreateInstrFormatTable();
1886
1887 if (wantUsage) {
1888 usage();
1889 return 2;
1890 }
1891
Andy McFadden0198b142009-04-02 14:48:56 -07001892 int result = 0;
1893 while (optind < argc) {
1894 result |= process(argv[optind++]);
1895 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001896
1897 free(gInstrWidth);
1898 free(gInstrFormat);
1899
Andy McFadden0198b142009-04-02 14:48:56 -07001900 return (result != 0);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001901}
Andy McFadden0198b142009-04-02 14:48:56 -07001902