blob: e8dcf61b706bb12893ce81df1fba3eea534bbafc [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/*
Carl Shapirode750892010-06-08 16:37:12 -070083 * Get 2 little-endian bytes.
84 */
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080085static inline u2 get2LE(unsigned char const* pSrc)
86{
87 return pSrc[0] | (pSrc[1] << 8);
Carl Shapirode750892010-06-08 16:37:12 -070088}
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080089
90/*
Carl Shapirode750892010-06-08 16:37:12 -070091 * Get 4 little-endian bytes.
92 */
The Android Open Source Project99409882009-03-18 22:20:24 -070093static inline u4 get4LE(unsigned char const* pSrc)
94{
95 return pSrc[0] | (pSrc[1] << 8) | (pSrc[2] << 16) | (pSrc[3] << 24);
Carl Shapirode750892010-06-08 16:37:12 -070096}
The Android Open Source Project99409882009-03-18 22:20:24 -070097
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.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800228 */
229static int countOnes(u4 val)
230{
231 int count = 0;
232
Cosmin Cojocare3393432010-04-18 18:25:06 +0200233 val = val - ((val >> 1) & 0x55555555);
234 val = (val & 0x33333333) + ((val >> 2) & 0x33333333);
235 count = (((val + (val >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800236
237 return count;
238}
239
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800240/*
241 * Flag for use with createAccessFlagStr().
242 */
243typedef enum AccessFor {
244 kAccessForClass = 0, kAccessForMethod = 1, kAccessForField = 2,
245 kAccessForMAX
246} AccessFor;
247
248/*
249 * Create a new string with human-readable access flags.
250 *
251 * In the base language the access_flags fields are type u2; in Dalvik
252 * they're u4.
253 */
254static char* createAccessFlagStr(u4 flags, AccessFor forWhat)
255{
256#define NUM_FLAGS 18
257 static const char* kAccessStrings[kAccessForMAX][NUM_FLAGS] = {
Carl Shapirode750892010-06-08 16:37:12 -0700258 {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800259 /* class, inner class */
260 "PUBLIC", /* 0x0001 */
261 "PRIVATE", /* 0x0002 */
262 "PROTECTED", /* 0x0004 */
263 "STATIC", /* 0x0008 */
264 "FINAL", /* 0x0010 */
265 "?", /* 0x0020 */
266 "?", /* 0x0040 */
267 "?", /* 0x0080 */
268 "?", /* 0x0100 */
269 "INTERFACE", /* 0x0200 */
270 "ABSTRACT", /* 0x0400 */
271 "?", /* 0x0800 */
272 "SYNTHETIC", /* 0x1000 */
273 "ANNOTATION", /* 0x2000 */
274 "ENUM", /* 0x4000 */
275 "?", /* 0x8000 */
276 "VERIFIED", /* 0x10000 */
277 "OPTIMIZED", /* 0x20000 */
278 },
279 {
280 /* method */
281 "PUBLIC", /* 0x0001 */
282 "PRIVATE", /* 0x0002 */
283 "PROTECTED", /* 0x0004 */
284 "STATIC", /* 0x0008 */
285 "FINAL", /* 0x0010 */
286 "SYNCHRONIZED", /* 0x0020 */
287 "BRIDGE", /* 0x0040 */
288 "VARARGS", /* 0x0080 */
289 "NATIVE", /* 0x0100 */
290 "?", /* 0x0200 */
291 "ABSTRACT", /* 0x0400 */
292 "STRICT", /* 0x0800 */
293 "SYNTHETIC", /* 0x1000 */
294 "?", /* 0x2000 */
295 "?", /* 0x4000 */
296 "MIRANDA", /* 0x8000 */
297 "CONSTRUCTOR", /* 0x10000 */
298 "DECLARED_SYNCHRONIZED", /* 0x20000 */
299 },
300 {
301 /* field */
302 "PUBLIC", /* 0x0001 */
303 "PRIVATE", /* 0x0002 */
304 "PROTECTED", /* 0x0004 */
305 "STATIC", /* 0x0008 */
306 "FINAL", /* 0x0010 */
307 "?", /* 0x0020 */
308 "VOLATILE", /* 0x0040 */
309 "TRANSIENT", /* 0x0080 */
310 "?", /* 0x0100 */
311 "?", /* 0x0200 */
312 "?", /* 0x0400 */
313 "?", /* 0x0800 */
314 "SYNTHETIC", /* 0x1000 */
315 "?", /* 0x2000 */
316 "ENUM", /* 0x4000 */
317 "?", /* 0x8000 */
318 "?", /* 0x10000 */
319 "?", /* 0x20000 */
320 },
321 };
322 const int kLongest = 21; /* strlen of longest string above */
323 int i, count;
324 char* str;
325 char* cp;
326
327 /*
328 * Allocate enough storage to hold the expected number of strings,
329 * plus a space between each. We over-allocate, using the longest
330 * string above as the base metric.
331 */
332 count = countOnes(flags);
333 cp = str = (char*) malloc(count * (kLongest+1) +1);
334
335 for (i = 0; i < NUM_FLAGS; i++) {
336 if (flags & 0x01) {
337 const char* accessStr = kAccessStrings[forWhat][i];
338 int len = strlen(accessStr);
339 if (cp != str)
340 *cp++ = ' ';
341
342 memcpy(cp, accessStr, len);
343 cp += len;
344 }
345 flags >>= 1;
346 }
347 *cp = '\0';
348
349 return str;
350}
351
352
353/*
Andy McFadden0ea77b92010-04-20 14:18:59 -0700354 * Copy character data from "data" to "out", converting non-ASCII values
355 * to printf format chars or an ASCII filler ('.' or '?').
356 *
357 * The output buffer must be able to hold (2*len)+1 bytes. The result is
358 * NUL-terminated.
359 */
360static void asciify(char* out, const unsigned char* data, size_t len)
361{
362 while (len--) {
363 if (*data < 0x20) {
364 /* could do more here, but we don't need them yet */
365 switch (*data) {
366 case '\0':
367 *out++ = '\\';
368 *out++ = '0';
369 break;
370 case '\n':
371 *out++ = '\\';
372 *out++ = 'n';
373 break;
374 default:
375 *out++ = '.';
376 break;
377 }
378 } else if (*data >= 0x80) {
379 *out++ = '?';
380 } else {
381 *out++ = *data;
382 }
383 data++;
384 }
385 *out = '\0';
386}
387
388/*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800389 * Dump the file header.
390 */
391void dumpFileHeader(const DexFile* pDexFile)
392{
Andy McFadden0ea77b92010-04-20 14:18:59 -0700393 const DexOptHeader* pOptHeader = pDexFile->pOptHeader;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800394 const DexHeader* pHeader = pDexFile->pHeader;
Andy McFadden0ea77b92010-04-20 14:18:59 -0700395 char sanitized[sizeof(pHeader->magic)*2 +1];
396
397 assert(sizeof(pHeader->magic) == sizeof(pOptHeader->magic));
398
399 if (pOptHeader != NULL) {
400 printf("Optimized DEX file header:\n");
401
402 asciify(sanitized, pOptHeader->magic, sizeof(pOptHeader->magic));
403 printf("magic : '%s'\n", sanitized);
404 printf("dex_offset : %d (0x%06x)\n",
405 pOptHeader->dexOffset, pOptHeader->dexOffset);
406 printf("dex_length : %d\n", pOptHeader->dexLength);
407 printf("deps_offset : %d (0x%06x)\n",
408 pOptHeader->depsOffset, pOptHeader->depsOffset);
409 printf("deps_length : %d\n", pOptHeader->depsLength);
410 printf("aux_offset : %d (0x%06x)\n",
411 pOptHeader->auxOffset, pOptHeader->auxOffset);
412 printf("aux_length : %d\n", pOptHeader->auxLength);
413 printf("flags : %08x\n", pOptHeader->flags);
414 printf("checksum : %08x\n", pOptHeader->checksum);
415 printf("\n");
416 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800417
418 printf("DEX file header:\n");
Andy McFadden0ea77b92010-04-20 14:18:59 -0700419 asciify(sanitized, pHeader->magic, sizeof(pHeader->magic));
420 printf("magic : '%s'\n", sanitized);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800421 printf("checksum : %08x\n", pHeader->checksum);
422 printf("signature : %02x%02x...%02x%02x\n",
423 pHeader->signature[0], pHeader->signature[1],
424 pHeader->signature[kSHA1DigestLen-2],
425 pHeader->signature[kSHA1DigestLen-1]);
426 printf("file_size : %d\n", pHeader->fileSize);
427 printf("header_size : %d\n", pHeader->headerSize);
428 printf("link_size : %d\n", pHeader->linkSize);
429 printf("link_off : %d (0x%06x)\n",
430 pHeader->linkOff, pHeader->linkOff);
431 printf("string_ids_size : %d\n", pHeader->stringIdsSize);
432 printf("string_ids_off : %d (0x%06x)\n",
433 pHeader->stringIdsOff, pHeader->stringIdsOff);
434 printf("type_ids_size : %d\n", pHeader->typeIdsSize);
435 printf("type_ids_off : %d (0x%06x)\n",
436 pHeader->typeIdsOff, pHeader->typeIdsOff);
437 printf("field_ids_size : %d\n", pHeader->fieldIdsSize);
438 printf("field_ids_off : %d (0x%06x)\n",
439 pHeader->fieldIdsOff, pHeader->fieldIdsOff);
440 printf("method_ids_size : %d\n", pHeader->methodIdsSize);
441 printf("method_ids_off : %d (0x%06x)\n",
442 pHeader->methodIdsOff, pHeader->methodIdsOff);
443 printf("class_defs_size : %d\n", pHeader->classDefsSize);
444 printf("class_defs_off : %d (0x%06x)\n",
445 pHeader->classDefsOff, pHeader->classDefsOff);
446 printf("data_size : %d\n", pHeader->dataSize);
447 printf("data_off : %d (0x%06x)\n",
448 pHeader->dataOff, pHeader->dataOff);
449 printf("\n");
450}
451
452/*
Andy McFadden0ea77b92010-04-20 14:18:59 -0700453 * Dump the "table of contents" for the aux area.
454 */
455void dumpAuxDirectory(const DexFile* pDexFile)
456{
457 const DexOptHeader* pOptHeader = pDexFile->pOptHeader;
458 if (pOptHeader == NULL)
459 return;
460
461 printf("AUX section contents:\n");
462
463 const u4* pAux = (const u4*) ((u1*) pOptHeader + pOptHeader->auxOffset);
464
465 if (*pAux == 0) {
466 printf("(1.0 format, only class lookup table is present)\n\n");
467 return;
468 }
469
470 /*
471 * The "aux" section is in "chunk" format: a 32-bit identifier, a 32-bit
472 * length, then the data. Chunks start on 64-bit boundaries.
473 */
474 while (*pAux != kDexChunkEnd) {
475 const char* verboseStr;
476
477 u4 size = *(pAux+1);
478
479 switch (*pAux) {
480 case kDexChunkClassLookup:
481 verboseStr = "class lookup hash table";
482 break;
483 case kDexChunkRegisterMaps:
484 verboseStr = "register maps";
485 break;
486 case kDexChunkReducingIndexMap:
487 verboseStr = "'reducing' index map";
488 break;
489 case kDexChunkExpandingIndexMap:
490 verboseStr = "'expanding' index map";
491 break;
492 default:
493 verboseStr = "(unknown chunk type)";
494 break;
495 }
496
497 printf("Chunk %08x (%c%c%c%c) - %s (%d bytes)\n", *pAux,
498 *pAux >> 24, (char)(*pAux >> 16), (char)(*pAux >> 8), (char)*pAux,
499 verboseStr, size);
500
501 size = (size + 8 + 7) & ~7;
502 pAux += size / sizeof(u4);
503 }
504 printf("\n");
505}
506
507/*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800508 * Dump a class_def_item.
509 */
510void dumpClassDef(DexFile* pDexFile, int idx)
511{
512 const DexClassDef* pClassDef;
513 const u1* pEncodedData;
514 DexClassData* pClassData;
515
516 pClassDef = dexGetClassDef(pDexFile, idx);
517 pEncodedData = dexGetClassData(pDexFile, pClassDef);
518 pClassData = dexReadAndVerifyClassData(&pEncodedData, NULL);
519
520 if (pClassData == NULL) {
521 fprintf(stderr, "Trouble reading class data\n");
522 return;
523 }
524
525 printf("Class #%d header:\n", idx);
526 printf("class_idx : %d\n", pClassDef->classIdx);
527 printf("access_flags : %d (0x%04x)\n",
528 pClassDef->accessFlags, pClassDef->accessFlags);
529 printf("superclass_idx : %d\n", pClassDef->superclassIdx);
530 printf("interfaces_off : %d (0x%06x)\n",
531 pClassDef->interfacesOff, pClassDef->interfacesOff);
532 printf("source_file_idx : %d\n", pClassDef->sourceFileIdx);
533 printf("annotations_off : %d (0x%06x)\n",
534 pClassDef->annotationsOff, pClassDef->annotationsOff);
535 printf("class_data_off : %d (0x%06x)\n",
536 pClassDef->classDataOff, pClassDef->classDataOff);
537 printf("static_fields_size : %d\n", pClassData->header.staticFieldsSize);
538 printf("instance_fields_size: %d\n",
539 pClassData->header.instanceFieldsSize);
540 printf("direct_methods_size : %d\n", pClassData->header.directMethodsSize);
541 printf("virtual_methods_size: %d\n",
542 pClassData->header.virtualMethodsSize);
543 printf("\n");
544
545 free(pClassData);
546}
547
548/*
Andy McFaddena2ee53b2009-05-05 16:52:10 -0700549 * Dump an interface that a class declares to implement.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800550 */
551void dumpInterface(const DexFile* pDexFile, const DexTypeItem* pTypeItem,
552 int i)
553{
554 const char* interfaceName =
555 dexStringByTypeIdx(pDexFile, pTypeItem->typeIdx);
556
Andy McFaddena2ee53b2009-05-05 16:52:10 -0700557 if (gOptions.outputFormat == OUTPUT_PLAIN) {
558 printf(" #%d : '%s'\n", i, interfaceName);
559 } else {
560 char* dotted = descriptorToDot(interfaceName);
561 printf("<implements name=\"%s\">\n</implements>\n", dotted);
562 free(dotted);
563 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800564}
565
566/*
567 * Dump the catches table associated with the code.
568 */
569void dumpCatches(DexFile* pDexFile, const DexCode* pCode)
570{
571 u4 triesSize = pCode->triesSize;
572
573 if (triesSize == 0) {
574 printf(" catches : (none)\n");
575 return;
Carl Shapirode750892010-06-08 16:37:12 -0700576 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800577
578 printf(" catches : %d\n", triesSize);
579
580 const DexTry* pTries = dexGetTries(pCode);
581 u4 i;
582
583 for (i = 0; i < triesSize; i++) {
584 const DexTry* pTry = &pTries[i];
585 u4 start = pTry->startAddr;
586 u4 end = start + pTry->insnCount;
587 DexCatchIterator iterator;
Carl Shapirode750892010-06-08 16:37:12 -0700588
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800589 printf(" 0x%04x - 0x%04x\n", start, end);
590
591 dexCatchIteratorInit(&iterator, pCode, pTry->handlerOff);
592
593 for (;;) {
594 DexCatchHandler* handler = dexCatchIteratorNext(&iterator);
595 const char* descriptor;
Carl Shapirode750892010-06-08 16:37:12 -0700596
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800597 if (handler == NULL) {
598 break;
599 }
Carl Shapirode750892010-06-08 16:37:12 -0700600
601 descriptor = (handler->typeIdx == kDexNoIndex) ? "<any>" :
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800602 dexStringByTypeIdx(pDexFile, handler->typeIdx);
Carl Shapirode750892010-06-08 16:37:12 -0700603
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800604 printf(" %s -> 0x%04x\n", descriptor,
605 handler->address);
606 }
607 }
608}
609
610static int dumpPositionsCb(void *cnxt, u4 address, u4 lineNum)
611{
612 printf(" 0x%04x line=%d\n", address, lineNum);
613 return 0;
614}
615
616/*
617 * Dump the positions list.
618 */
Carl Shapirode750892010-06-08 16:37:12 -0700619void dumpPositions(DexFile* pDexFile, const DexCode* pCode,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800620 const DexMethod *pDexMethod)
621{
622 printf(" positions : \n");
Carl Shapirode750892010-06-08 16:37:12 -0700623 const DexMethodId *pMethodId
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800624 = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
625 const char *classDescriptor
626 = dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
627
628 dexDecodeDebugInfo(pDexFile, pCode, classDescriptor, pMethodId->protoIdx,
629 pDexMethod->accessFlags, dumpPositionsCb, NULL, NULL);
630}
631
632static void dumpLocalsCb(void *cnxt, u2 reg, u4 startAddress,
633 u4 endAddress, const char *name, const char *descriptor,
634 const char *signature)
635{
636 printf(" 0x%04x - 0x%04x reg=%d %s %s %s\n",
Carl Shapirode750892010-06-08 16:37:12 -0700637 startAddress, endAddress, reg, name, descriptor,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800638 signature);
639}
640
641/*
642 * Dump the locals list.
643 */
644void dumpLocals(DexFile* pDexFile, const DexCode* pCode,
645 const DexMethod *pDexMethod)
646{
647 printf(" locals : \n");
648
Carl Shapirode750892010-06-08 16:37:12 -0700649 const DexMethodId *pMethodId
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800650 = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
Carl Shapirode750892010-06-08 16:37:12 -0700651 const char *classDescriptor
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800652 = dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
653
654 dexDecodeDebugInfo(pDexFile, pCode, classDescriptor, pMethodId->protoIdx,
655 pDexMethod->accessFlags, NULL, dumpLocalsCb, NULL);
656}
657
658/*
659 * Get information about a method.
660 */
661bool getMethodInfo(DexFile* pDexFile, u4 methodIdx, FieldMethodInfo* pMethInfo)
662{
663 const DexMethodId* pMethodId;
664
665 if (methodIdx >= pDexFile->pHeader->methodIdsSize)
666 return false;
667
668 pMethodId = dexGetMethodId(pDexFile, methodIdx);
669 pMethInfo->name = dexStringById(pDexFile, pMethodId->nameIdx);
670 pMethInfo->signature = dexCopyDescriptorFromMethodId(pDexFile, pMethodId);
671
Carl Shapirode750892010-06-08 16:37:12 -0700672 pMethInfo->classDescriptor =
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800673 dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
674 return true;
675}
676
677/*
678 * Get information about a field.
679 */
680bool getFieldInfo(DexFile* pDexFile, u4 fieldIdx, FieldMethodInfo* pFieldInfo)
681{
682 const DexFieldId* pFieldId;
683
684 if (fieldIdx >= pDexFile->pHeader->fieldIdsSize)
685 return false;
686
687 pFieldId = dexGetFieldId(pDexFile, fieldIdx);
688 pFieldInfo->name = dexStringById(pDexFile, pFieldId->nameIdx);
689 pFieldInfo->signature = dexStringByTypeIdx(pDexFile, pFieldId->typeIdx);
690 pFieldInfo->classDescriptor =
691 dexStringByTypeIdx(pDexFile, pFieldId->classIdx);
692 return true;
693}
694
695
696/*
697 * Look up a class' descriptor.
698 */
699const char* getClassDescriptor(DexFile* pDexFile, u4 classIdx)
700{
701 return dexStringByTypeIdx(pDexFile, classIdx);
702}
703
704/*
705 * Dump a single instruction.
706 */
707void dumpInstruction(DexFile* pDexFile, const DexCode* pCode, int insnIdx,
708 int insnWidth, const DecodedInstruction* pDecInsn)
709{
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800710 const u2* insns = pCode->insns;
711 int i;
712
713 printf("%06x:", ((u1*)insns - pDexFile->baseAddr) + insnIdx*2);
714 for (i = 0; i < 8; i++) {
715 if (i < insnWidth) {
716 if (i == 7) {
717 printf(" ... ");
718 } else {
719 /* print 16-bit value in little-endian order */
720 const u1* bytePtr = (const u1*) &insns[insnIdx+i];
721 printf(" %02x%02x", bytePtr[0], bytePtr[1]);
722 }
723 } else {
724 fputs(" ", stdout);
725 }
726 }
727
728 if (pDecInsn->opCode == OP_NOP) {
729 u2 instr = get2LE((const u1*) &insns[insnIdx]);
730 if (instr == kPackedSwitchSignature) {
731 printf("|%04x: packed-switch-data (%d units)",
732 insnIdx, insnWidth);
733 } else if (instr == kSparseSwitchSignature) {
734 printf("|%04x: sparse-switch-data (%d units)",
735 insnIdx, insnWidth);
736 } else if (instr == kArrayDataSignature) {
737 printf("|%04x: array-data (%d units)",
738 insnIdx, insnWidth);
739 } else {
740 printf("|%04x: nop // spacer", insnIdx);
741 }
742 } else {
743 printf("|%04x: %s", insnIdx, getOpcodeName(pDecInsn->opCode));
744 }
745
746 switch (dexGetInstrFormat(gInstrFormat, pDecInsn->opCode)) {
747 case kFmt10x: // op
748 break;
749 case kFmt12x: // op vA, vB
750 printf(" v%d, v%d", pDecInsn->vA, pDecInsn->vB);
751 break;
752 case kFmt11n: // op vA, #+B
753 printf(" v%d, #int %d // #%x",
754 pDecInsn->vA, (s4)pDecInsn->vB, (u1)pDecInsn->vB);
755 break;
756 case kFmt11x: // op vAA
757 printf(" v%d", pDecInsn->vA);
758 break;
759 case kFmt10t: // op +AA
760 case kFmt20t: // op +AAAA
761 {
762 s4 targ = (s4) pDecInsn->vA;
763 printf(" %04x // %c%04x",
764 insnIdx + targ,
765 (targ < 0) ? '-' : '+',
766 (targ < 0) ? -targ : targ);
767 }
768 break;
769 case kFmt22x: // op vAA, vBBBB
770 printf(" v%d, v%d", pDecInsn->vA, pDecInsn->vB);
771 break;
772 case kFmt21t: // op vAA, +BBBB
773 {
774 s4 targ = (s4) pDecInsn->vB;
775 printf(" v%d, %04x // %c%04x", pDecInsn->vA,
776 insnIdx + targ,
777 (targ < 0) ? '-' : '+',
778 (targ < 0) ? -targ : targ);
779 }
780 break;
781 case kFmt21s: // op vAA, #+BBBB
782 printf(" v%d, #int %d // #%x",
783 pDecInsn->vA, (s4)pDecInsn->vB, (u2)pDecInsn->vB);
784 break;
785 case kFmt21h: // op vAA, #+BBBB0000[00000000]
786 // The printed format varies a bit based on the actual opcode.
787 if (pDecInsn->opCode == OP_CONST_HIGH16) {
788 s4 value = pDecInsn->vB << 16;
789 printf(" v%d, #int %d // #%x",
790 pDecInsn->vA, value, (u2)pDecInsn->vB);
791 } else {
792 s8 value = ((s8) pDecInsn->vB) << 48;
793 printf(" v%d, #long %lld // #%x",
794 pDecInsn->vA, value, (u2)pDecInsn->vB);
795 }
796 break;
797 case kFmt21c: // op vAA, thing@BBBB
798 if (pDecInsn->opCode == OP_CONST_STRING) {
799 printf(" v%d, \"%s\" // string@%04x", pDecInsn->vA,
800 dexStringById(pDexFile, pDecInsn->vB), pDecInsn->vB);
801 } else if (pDecInsn->opCode == OP_CHECK_CAST ||
802 pDecInsn->opCode == OP_NEW_INSTANCE ||
803 pDecInsn->opCode == OP_CONST_CLASS)
804 {
805 printf(" v%d, %s // class@%04x", pDecInsn->vA,
806 getClassDescriptor(pDexFile, pDecInsn->vB), pDecInsn->vB);
807 } else /* OP_SGET* */ {
808 FieldMethodInfo fieldInfo;
809 if (getFieldInfo(pDexFile, pDecInsn->vB, &fieldInfo)) {
810 printf(" v%d, %s.%s:%s // field@%04x", pDecInsn->vA,
811 fieldInfo.classDescriptor, fieldInfo.name,
812 fieldInfo.signature, pDecInsn->vB);
813 } else {
814 printf(" v%d, ??? // field@%04x", pDecInsn->vA, pDecInsn->vB);
815 }
816 }
817 break;
818 case kFmt23x: // op vAA, vBB, vCC
819 printf(" v%d, v%d, v%d", pDecInsn->vA, pDecInsn->vB, pDecInsn->vC);
820 break;
821 case kFmt22b: // op vAA, vBB, #+CC
822 printf(" v%d, v%d, #int %d // #%02x",
823 pDecInsn->vA, pDecInsn->vB, (s4)pDecInsn->vC, (u1)pDecInsn->vC);
824 break;
825 case kFmt22t: // op vA, vB, +CCCC
826 {
827 s4 targ = (s4) pDecInsn->vC;
828 printf(" v%d, v%d, %04x // %c%04x", pDecInsn->vA, pDecInsn->vB,
829 insnIdx + targ,
830 (targ < 0) ? '-' : '+',
831 (targ < 0) ? -targ : targ);
832 }
833 break;
834 case kFmt22s: // op vA, vB, #+CCCC
835 printf(" v%d, v%d, #int %d // #%04x",
836 pDecInsn->vA, pDecInsn->vB, (s4)pDecInsn->vC, (u2)pDecInsn->vC);
837 break;
838 case kFmt22c: // op vA, vB, thing@CCCC
839 if (pDecInsn->opCode >= OP_IGET && pDecInsn->opCode <= OP_IPUT_SHORT) {
840 FieldMethodInfo fieldInfo;
841 if (getFieldInfo(pDexFile, pDecInsn->vC, &fieldInfo)) {
842 printf(" v%d, v%d, %s.%s:%s // field@%04x", pDecInsn->vA,
843 pDecInsn->vB, fieldInfo.classDescriptor, fieldInfo.name,
844 fieldInfo.signature, pDecInsn->vC);
845 } else {
846 printf(" v%d, v%d, ??? // field@%04x", pDecInsn->vA,
847 pDecInsn->vB, pDecInsn->vC);
848 }
849 } else {
850 printf(" v%d, v%d, %s // class@%04x",
851 pDecInsn->vA, pDecInsn->vB,
852 getClassDescriptor(pDexFile, pDecInsn->vC), pDecInsn->vC);
853 }
854 break;
855 case kFmt22cs: // [opt] op vA, vB, field offset CCCC
856 printf(" v%d, v%d, [obj+%04x]",
857 pDecInsn->vA, pDecInsn->vB, pDecInsn->vC);
858 break;
859 case kFmt30t:
860 printf(" #%08x", pDecInsn->vA);
861 break;
862 case kFmt31i: // op vAA, #+BBBBBBBB
863 {
864 /* this is often, but not always, a float */
865 union {
866 float f;
867 u4 i;
868 } conv;
869 conv.i = pDecInsn->vB;
870 printf(" v%d, #float %f // #%08x",
871 pDecInsn->vA, conv.f, pDecInsn->vB);
872 }
873 break;
874 case kFmt31c: // op vAA, thing@BBBBBBBB
875 printf(" v%d, \"%s\" // string@%08x", pDecInsn->vA,
876 dexStringById(pDexFile, pDecInsn->vB), pDecInsn->vB);
877 break;
878 case kFmt31t: // op vAA, offset +BBBBBBBB
879 printf(" v%d, %08x // +%08x",
880 pDecInsn->vA, insnIdx + pDecInsn->vB, pDecInsn->vB);
881 break;
882 case kFmt32x: // op vAAAA, vBBBB
883 printf(" v%d, v%d", pDecInsn->vA, pDecInsn->vB);
884 break;
885 case kFmt35c: // op vB, {vD, vE, vF, vG, vA}, thing@CCCC
886 {
887 /* NOTE: decoding of 35c doesn't quite match spec */
888 fputs(" {", stdout);
889 for (i = 0; i < (int) pDecInsn->vA; i++) {
890 if (i == 0)
891 printf("v%d", pDecInsn->arg[i]);
892 else
893 printf(", v%d", pDecInsn->arg[i]);
894 }
895 if (pDecInsn->opCode == OP_FILLED_NEW_ARRAY) {
896 printf("}, %s // class@%04x",
897 getClassDescriptor(pDexFile, pDecInsn->vB), pDecInsn->vB);
898 } else {
899 FieldMethodInfo methInfo;
900 if (getMethodInfo(pDexFile, pDecInsn->vB, &methInfo)) {
901 printf("}, %s.%s:%s // method@%04x",
902 methInfo.classDescriptor, methInfo.name,
903 methInfo.signature, pDecInsn->vB);
904 } else {
905 printf("}, ??? // method@%04x", pDecInsn->vB);
906 }
907 }
908 }
909 break;
910 case kFmt35ms: // [opt] invoke-virtual+super
911 case kFmt35fs: // [opt] invoke-interface
912 {
913 fputs(" {", stdout);
914 for (i = 0; i < (int) pDecInsn->vA; i++) {
915 if (i == 0)
916 printf("v%d", pDecInsn->arg[i]);
917 else
918 printf(", v%d", pDecInsn->arg[i]);
919 }
920 printf("}, [%04x] // vtable #%04x", pDecInsn->vB, pDecInsn->vB);
921 }
922 break;
923 case kFmt3rc: // op {vCCCC .. v(CCCC+AA-1)}, meth@BBBB
924 {
925 /*
926 * This doesn't match the "dx" output when some of the args are
927 * 64-bit values -- dx only shows the first register.
928 */
929 fputs(" {", stdout);
930 for (i = 0; i < (int) pDecInsn->vA; i++) {
931 if (i == 0)
932 printf("v%d", pDecInsn->vC + i);
933 else
934 printf(", v%d", pDecInsn->vC + i);
935 }
936 if (pDecInsn->opCode == OP_FILLED_NEW_ARRAY_RANGE) {
937 printf("}, %s // class@%04x",
938 getClassDescriptor(pDexFile, pDecInsn->vB), pDecInsn->vB);
939 } else {
940 FieldMethodInfo methInfo;
941 if (getMethodInfo(pDexFile, pDecInsn->vB, &methInfo)) {
942 printf("}, %s.%s:%s // method@%04x",
943 methInfo.classDescriptor, methInfo.name,
944 methInfo.signature, pDecInsn->vB);
945 } else {
946 printf("}, ??? // method@%04x", pDecInsn->vB);
947 }
948 }
949 }
950 break;
951 case kFmt3rms: // [opt] invoke-virtual+super/range
952 case kFmt3rfs: // [opt] invoke-interface/range
953 {
954 /*
955 * This doesn't match the "dx" output when some of the args are
956 * 64-bit values -- dx only shows the first register.
957 */
958 fputs(" {", stdout);
959 for (i = 0; i < (int) pDecInsn->vA; i++) {
960 if (i == 0)
961 printf("v%d", pDecInsn->vC + i);
962 else
963 printf(", v%d", pDecInsn->vC + i);
964 }
965 printf("}, [%04x] // vtable #%04x", pDecInsn->vB, pDecInsn->vB);
966 }
967 break;
Andy McFaddenb0a05412009-11-19 10:23:41 -0800968 case kFmt3rinline: // [opt] execute-inline/range
969 {
970 fputs(" {", stdout);
971 for (i = 0; i < (int) pDecInsn->vA; i++) {
972 if (i == 0)
973 printf("v%d", pDecInsn->vC + i);
974 else
975 printf(", v%d", pDecInsn->vC + i);
976 }
977 printf("}, [%04x] // inline #%04x", pDecInsn->vB, pDecInsn->vB);
978 }
979 break;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800980 case kFmt3inline: // [opt] inline invoke
981 {
982#if 0
983 const InlineOperation* inlineOpsTable = dvmGetInlineOpsTable();
984 u4 tableLen = dvmGetInlineOpsTableLength();
985#endif
986
987 fputs(" {", stdout);
988 for (i = 0; i < (int) pDecInsn->vA; i++) {
989 if (i == 0)
990 printf("v%d", pDecInsn->arg[i]);
991 else
992 printf(", v%d", pDecInsn->arg[i]);
993 }
994#if 0
995 if (pDecInsn->vB < tableLen) {
996 printf("}, %s.%s:%s // inline #%04x",
997 inlineOpsTable[pDecInsn->vB].classDescriptor,
998 inlineOpsTable[pDecInsn->vB].methodName,
999 inlineOpsTable[pDecInsn->vB].methodSignature,
1000 pDecInsn->vB);
1001 } else {
1002#endif
1003 printf("}, [%04x] // inline #%04x", pDecInsn->vB, pDecInsn->vB);
1004#if 0
1005 }
1006#endif
1007 }
1008 break;
1009 case kFmt51l: // op vAA, #+BBBBBBBBBBBBBBBB
1010 {
1011 /* this is often, but not always, a double */
1012 union {
1013 double d;
1014 u8 j;
1015 } conv;
1016 conv.j = pDecInsn->vB_wide;
1017 printf(" v%d, #double %f // #%016llx",
1018 pDecInsn->vA, conv.d, pDecInsn->vB_wide);
1019 }
1020 break;
1021 case kFmtUnknown:
1022 break;
1023 default:
1024 printf(" ???");
1025 break;
1026 }
1027
1028
1029 putchar('\n');
1030
1031}
1032
1033/*
1034 * Dump a bytecode disassembly.
1035 */
1036void dumpBytecodes(DexFile* pDexFile, const DexMethod* pDexMethod)
1037{
1038 const DexCode* pCode = dexGetCode(pDexFile, pDexMethod);
1039 const u2* insns;
1040 int insnIdx;
1041 FieldMethodInfo methInfo;
1042 int startAddr;
1043 char* className = NULL;
1044
1045 assert(pCode->insnsSize > 0);
1046 insns = pCode->insns;
1047
1048 getMethodInfo(pDexFile, pDexMethod->methodIdx, &methInfo);
1049 startAddr = ((u1*)pCode - pDexFile->baseAddr);
1050 className = descriptorToDot(methInfo.classDescriptor);
1051
1052 printf("%06x: |[%06x] %s.%s:%s\n",
1053 startAddr, startAddr,
1054 className, methInfo.name, methInfo.signature);
1055
1056 insnIdx = 0;
1057 while (insnIdx < (int) pCode->insnsSize) {
1058 int insnWidth;
1059 OpCode opCode;
1060 DecodedInstruction decInsn;
1061 u2 instr;
1062
1063 instr = get2LE((const u1*)insns);
1064 if (instr == kPackedSwitchSignature) {
1065 insnWidth = 4 + get2LE((const u1*)(insns+1)) * 2;
1066 } else if (instr == kSparseSwitchSignature) {
1067 insnWidth = 2 + get2LE((const u1*)(insns+1)) * 4;
1068 } else if (instr == kArrayDataSignature) {
1069 int width = get2LE((const u1*)(insns+1));
Carl Shapirode750892010-06-08 16:37:12 -07001070 int size = get2LE((const u1*)(insns+2)) |
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001071 (get2LE((const u1*)(insns+3))<<16);
Carl Shapirode750892010-06-08 16:37:12 -07001072 // The plus 1 is to round up for odd size and width
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001073 insnWidth = 4 + ((size * width) + 1) / 2;
1074 } else {
1075 opCode = instr & 0xff;
1076 insnWidth = dexGetInstrWidthAbs(gInstrWidth, opCode);
1077 if (insnWidth == 0) {
1078 fprintf(stderr,
1079 "GLITCH: zero-width instruction at idx=0x%04x\n", insnIdx);
1080 break;
1081 }
1082 }
1083
1084 dexDecodeInstruction(gInstrFormat, insns, &decInsn);
1085 dumpInstruction(pDexFile, pCode, insnIdx, insnWidth, &decInsn);
1086
1087 insns += insnWidth;
1088 insnIdx += insnWidth;
1089 }
1090
1091 free(className);
1092}
1093
1094/*
1095 * Dump a "code" struct.
1096 */
1097void dumpCode(DexFile* pDexFile, const DexMethod* pDexMethod)
1098{
1099 const DexCode* pCode = dexGetCode(pDexFile, pDexMethod);
1100
1101 printf(" registers : %d\n", pCode->registersSize);
1102 printf(" ins : %d\n", pCode->insSize);
1103 printf(" outs : %d\n", pCode->outsSize);
1104 printf(" insns size : %d 16-bit code units\n", pCode->insnsSize);
1105
1106 if (gOptions.disassemble)
1107 dumpBytecodes(pDexFile, pDexMethod);
1108
1109 dumpCatches(pDexFile, pCode);
1110 /* both of these are encoded in debug info */
1111 dumpPositions(pDexFile, pCode, pDexMethod);
1112 dumpLocals(pDexFile, pCode, pDexMethod);
1113}
1114
1115/*
1116 * Dump a method.
1117 */
1118void dumpMethod(DexFile* pDexFile, const DexMethod* pDexMethod, int i)
1119{
1120 const DexMethodId* pMethodId;
1121 const char* backDescriptor;
1122 const char* name;
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001123 char* typeDescriptor = NULL;
1124 char* accessStr = NULL;
1125
1126 if (gOptions.exportsOnly &&
1127 (pDexMethod->accessFlags & (ACC_PUBLIC | ACC_PROTECTED)) == 0)
1128 {
1129 return;
1130 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001131
1132 pMethodId = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
1133 name = dexStringById(pDexFile, pMethodId->nameIdx);
1134 typeDescriptor = dexCopyDescriptorFromMethodId(pDexFile, pMethodId);
1135
1136 backDescriptor = dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
1137
1138 accessStr = createAccessFlagStr(pDexMethod->accessFlags,
1139 kAccessForMethod);
1140
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001141 if (gOptions.outputFormat == OUTPUT_PLAIN) {
1142 printf(" #%d : (in %s)\n", i, backDescriptor);
1143 printf(" name : '%s'\n", name);
1144 printf(" type : '%s'\n", typeDescriptor);
1145 printf(" access : 0x%04x (%s)\n",
1146 pDexMethod->accessFlags, accessStr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001147
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001148 if (pDexMethod->codeOff == 0) {
1149 printf(" code : (none)\n");
1150 } else {
1151 printf(" code -\n");
1152 dumpCode(pDexFile, pDexMethod);
1153 }
1154
1155 if (gOptions.disassemble)
1156 putchar('\n');
1157 } else if (gOptions.outputFormat == OUTPUT_XML) {
1158 bool constructor = (name[0] == '<');
1159
1160 if (constructor) {
1161 char* tmp;
1162
1163 tmp = descriptorClassToDot(backDescriptor);
1164 printf("<constructor name=\"%s\"\n", tmp);
1165 free(tmp);
1166
1167 tmp = descriptorToDot(backDescriptor);
1168 printf(" type=\"%s\"\n", tmp);
1169 free(tmp);
1170 } else {
1171 printf("<method name=\"%s\"\n", name);
1172
1173 const char* returnType = strrchr(typeDescriptor, ')');
1174 if (returnType == NULL) {
1175 fprintf(stderr, "bad method type descriptor '%s'\n",
1176 typeDescriptor);
1177 goto bail;
1178 }
1179
1180 char* tmp = descriptorToDot(returnType+1);
1181 printf(" return=\"%s\"\n", tmp);
1182 free(tmp);
1183
1184 printf(" abstract=%s\n",
1185 quotedBool((pDexMethod->accessFlags & ACC_ABSTRACT) != 0));
1186 printf(" native=%s\n",
1187 quotedBool((pDexMethod->accessFlags & ACC_NATIVE) != 0));
1188
1189 bool isSync =
1190 (pDexMethod->accessFlags & ACC_SYNCHRONIZED) != 0 ||
1191 (pDexMethod->accessFlags & ACC_DECLARED_SYNCHRONIZED) != 0;
1192 printf(" synchronized=%s\n", quotedBool(isSync));
1193 }
1194
1195 printf(" static=%s\n",
1196 quotedBool((pDexMethod->accessFlags & ACC_STATIC) != 0));
1197 printf(" final=%s\n",
1198 quotedBool((pDexMethod->accessFlags & ACC_FINAL) != 0));
1199 // "deprecated=" not knowable w/o parsing annotations
1200 printf(" visibility=%s\n",
1201 quotedVisibility(pDexMethod->accessFlags));
1202
1203 printf(">\n");
1204
1205 /*
1206 * Parameters.
1207 */
1208 if (typeDescriptor[0] != '(') {
1209 fprintf(stderr, "ERROR: bad descriptor '%s'\n", typeDescriptor);
1210 goto bail;
1211 }
1212
1213 char tmpBuf[strlen(typeDescriptor)+1]; /* more than big enough */
1214 int argNum = 0;
1215
1216 const char* base = typeDescriptor+1;
1217
1218 while (*base != ')') {
1219 char* cp = tmpBuf;
1220
1221 while (*base == '[')
1222 *cp++ = *base++;
1223
1224 if (*base == 'L') {
1225 /* copy through ';' */
1226 do {
1227 *cp = *base++;
1228 } while (*cp++ != ';');
1229 } else {
1230 /* primitive char, copy it */
1231 if (strchr("ZBCSIFJD", *base) == NULL) {
1232 fprintf(stderr, "ERROR: bad method signature '%s'\n", base);
1233 goto bail;
1234 }
1235 *cp++ = *base++;
1236 }
1237
1238 /* null terminate and display */
1239 *cp++ = '\0';
1240
1241 char* tmp = descriptorToDot(tmpBuf);
1242 printf("<parameter name=\"arg%d\" type=\"%s\">\n</parameter>\n",
1243 argNum++, tmp);
1244 free(tmp);
1245 }
1246
1247 if (constructor)
1248 printf("</constructor>\n");
1249 else
1250 printf("</method>\n");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001251 }
1252
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001253bail:
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001254 free(typeDescriptor);
1255 free(accessStr);
1256}
1257
1258/*
1259 * Dump a static (class) field.
1260 */
1261void dumpSField(const DexFile* pDexFile, const DexField* pSField, int i)
1262{
1263 const DexFieldId* pFieldId;
1264 const char* backDescriptor;
1265 const char* name;
1266 const char* typeDescriptor;
1267 char* accessStr;
1268
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001269 if (gOptions.exportsOnly &&
1270 (pSField->accessFlags & (ACC_PUBLIC | ACC_PROTECTED)) == 0)
1271 {
1272 return;
1273 }
1274
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001275 pFieldId = dexGetFieldId(pDexFile, pSField->fieldIdx);
1276 name = dexStringById(pDexFile, pFieldId->nameIdx);
1277 typeDescriptor = dexStringByTypeIdx(pDexFile, pFieldId->typeIdx);
1278 backDescriptor = dexStringByTypeIdx(pDexFile, pFieldId->classIdx);
1279
1280 accessStr = createAccessFlagStr(pSField->accessFlags, kAccessForField);
1281
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001282 if (gOptions.outputFormat == OUTPUT_PLAIN) {
1283 printf(" #%d : (in %s)\n", i, backDescriptor);
1284 printf(" name : '%s'\n", name);
1285 printf(" type : '%s'\n", typeDescriptor);
1286 printf(" access : 0x%04x (%s)\n",
1287 pSField->accessFlags, accessStr);
1288 } else if (gOptions.outputFormat == OUTPUT_XML) {
1289 char* tmp;
1290
1291 printf("<field name=\"%s\"\n", name);
1292
1293 tmp = descriptorToDot(typeDescriptor);
1294 printf(" type=\"%s\"\n", tmp);
1295 free(tmp);
1296
1297 printf(" transient=%s\n",
1298 quotedBool((pSField->accessFlags & ACC_TRANSIENT) != 0));
1299 printf(" volatile=%s\n",
1300 quotedBool((pSField->accessFlags & ACC_VOLATILE) != 0));
1301 // "value=" not knowable w/o parsing annotations
1302 printf(" static=%s\n",
1303 quotedBool((pSField->accessFlags & ACC_STATIC) != 0));
1304 printf(" final=%s\n",
1305 quotedBool((pSField->accessFlags & ACC_FINAL) != 0));
1306 // "deprecated=" not knowable w/o parsing annotations
1307 printf(" visibility=%s\n",
1308 quotedVisibility(pSField->accessFlags));
1309 printf(">\n</field>\n");
1310 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001311
1312 free(accessStr);
1313}
1314
1315/*
1316 * Dump an instance field.
1317 */
1318void dumpIField(const DexFile* pDexFile, const DexField* pIField, int i)
1319{
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001320 dumpSField(pDexFile, pIField, i);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001321}
1322
1323/*
1324 * Dump the class.
The Android Open Source Project99409882009-03-18 22:20:24 -07001325 *
1326 * Note "idx" is a DexClassDef index, not a DexTypeId index.
Andy McFaddend18aff32009-05-06 10:19:16 -07001327 *
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001328 * If "*pLastPackage" is NULL or does not match the current class' package,
1329 * the value will be replaced with a newly-allocated string.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001330 */
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001331void dumpClass(DexFile* pDexFile, int idx, char** pLastPackage)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001332{
1333 const DexTypeList* pInterfaces;
1334 const DexClassDef* pClassDef;
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001335 DexClassData* pClassData = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001336 const u1* pEncodedData;
1337 const char* fileName;
1338 const char* classDescriptor;
1339 const char* superclassDescriptor;
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001340 char* accessStr = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001341 int i;
1342
1343 pClassDef = dexGetClassDef(pDexFile, idx);
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001344
1345 if (gOptions.exportsOnly && (pClassDef->accessFlags & ACC_PUBLIC) == 0) {
1346 //printf("<!-- omitting non-public class %s -->\n",
1347 // classDescriptor);
1348 goto bail;
1349 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001350
1351 pEncodedData = dexGetClassData(pDexFile, pClassDef);
1352 pClassData = dexReadAndVerifyClassData(&pEncodedData, NULL);
1353
1354 if (pClassData == NULL) {
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001355 printf("Trouble reading class data (#%d)\n", idx);
1356 goto bail;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001357 }
Carl Shapirode750892010-06-08 16:37:12 -07001358
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001359 classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001360
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001361 /*
1362 * For the XML output, show the package name. Ideally we'd gather
1363 * up the classes, sort them, and dump them alphabetically so the
1364 * package name wouldn't jump around, but that's not a great plan
1365 * for something that needs to run on the device.
1366 */
1367 if (!(classDescriptor[0] == 'L' &&
1368 classDescriptor[strlen(classDescriptor)-1] == ';'))
1369 {
1370 /* arrays and primitives should not be defined explicitly */
1371 fprintf(stderr, "Malformed class name '%s'\n", classDescriptor);
1372 /* keep going? */
1373 } else if (gOptions.outputFormat == OUTPUT_XML) {
1374 char* mangle;
1375 char* lastSlash;
1376 char* cp;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001377
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001378 mangle = strdup(classDescriptor + 1);
1379 mangle[strlen(mangle)-1] = '\0';
1380
1381 /* reduce to just the package name */
1382 lastSlash = strrchr(mangle, '/');
1383 if (lastSlash != NULL) {
1384 *lastSlash = '\0';
1385 } else {
1386 *mangle = '\0';
1387 }
1388
1389 for (cp = mangle; *cp != '\0'; cp++) {
1390 if (*cp == '/')
1391 *cp = '.';
1392 }
1393
1394 if (*pLastPackage == NULL || strcmp(mangle, *pLastPackage) != 0) {
1395 /* start of a new package */
1396 if (*pLastPackage != NULL)
1397 printf("</package>\n");
1398 printf("<package name=\"%s\"\n>\n", mangle);
1399 free(*pLastPackage);
1400 *pLastPackage = mangle;
1401 } else {
1402 free(mangle);
1403 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001404 }
1405
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001406 accessStr = createAccessFlagStr(pClassDef->accessFlags, kAccessForClass);
1407
1408 if (pClassDef->superclassIdx == kDexNoIndex) {
1409 superclassDescriptor = NULL;
1410 } else {
1411 superclassDescriptor =
1412 dexStringByTypeIdx(pDexFile, pClassDef->superclassIdx);
1413 }
1414
1415 if (gOptions.outputFormat == OUTPUT_PLAIN) {
1416 printf("Class #%d -\n", idx);
1417 printf(" Class descriptor : '%s'\n", classDescriptor);
1418 printf(" Access flags : 0x%04x (%s)\n",
1419 pClassDef->accessFlags, accessStr);
1420
1421 if (superclassDescriptor != NULL)
1422 printf(" Superclass : '%s'\n", superclassDescriptor);
1423
1424 printf(" Interfaces -\n");
1425 } else {
1426 char* tmp;
1427
1428 tmp = descriptorClassToDot(classDescriptor);
1429 printf("<class name=\"%s\"\n", tmp);
1430 free(tmp);
1431
1432 if (superclassDescriptor != NULL) {
1433 tmp = descriptorToDot(superclassDescriptor);
1434 printf(" extends=\"%s\"\n", tmp);
1435 free(tmp);
1436 }
1437 printf(" abstract=%s\n",
1438 quotedBool((pClassDef->accessFlags & ACC_ABSTRACT) != 0));
1439 printf(" static=%s\n",
1440 quotedBool((pClassDef->accessFlags & ACC_STATIC) != 0));
1441 printf(" final=%s\n",
1442 quotedBool((pClassDef->accessFlags & ACC_FINAL) != 0));
1443 // "deprecated=" not knowable w/o parsing annotations
1444 printf(" visibility=%s\n",
1445 quotedVisibility(pClassDef->accessFlags));
1446 printf(">\n");
1447 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001448 pInterfaces = dexGetInterfacesList(pDexFile, pClassDef);
1449 if (pInterfaces != NULL) {
1450 for (i = 0; i < (int) pInterfaces->size; i++)
1451 dumpInterface(pDexFile, dexGetTypeItem(pInterfaces, i), i);
1452 }
1453
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001454 if (gOptions.outputFormat == OUTPUT_PLAIN)
1455 printf(" Static fields -\n");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001456 for (i = 0; i < (int) pClassData->header.staticFieldsSize; i++) {
1457 dumpSField(pDexFile, &pClassData->staticFields[i], i);
1458 }
1459
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001460 if (gOptions.outputFormat == OUTPUT_PLAIN)
1461 printf(" Instance fields -\n");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001462 for (i = 0; i < (int) pClassData->header.instanceFieldsSize; i++) {
1463 dumpIField(pDexFile, &pClassData->instanceFields[i], i);
1464 }
1465
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001466 if (gOptions.outputFormat == OUTPUT_PLAIN)
1467 printf(" Direct methods -\n");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001468 for (i = 0; i < (int) pClassData->header.directMethodsSize; i++) {
1469 dumpMethod(pDexFile, &pClassData->directMethods[i], i);
1470 }
1471
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001472 if (gOptions.outputFormat == OUTPUT_PLAIN)
1473 printf(" Virtual methods -\n");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001474 for (i = 0; i < (int) pClassData->header.virtualMethodsSize; i++) {
1475 dumpMethod(pDexFile, &pClassData->virtualMethods[i], i);
1476 }
1477
1478 // TODO: Annotations.
1479
1480 if (pClassDef->sourceFileIdx != kDexNoIndex)
1481 fileName = dexStringById(pDexFile, pClassDef->sourceFileIdx);
1482 else
1483 fileName = "unknown";
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001484
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001485 if (gOptions.outputFormat == OUTPUT_PLAIN) {
1486 printf(" source_file_idx : %d (%s)\n",
1487 pClassDef->sourceFileIdx, fileName);
1488 printf("\n");
1489 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001490
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001491 if (gOptions.outputFormat == OUTPUT_XML) {
1492 printf("</class>\n");
1493 }
1494
1495bail:
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001496 free(pClassData);
1497 free(accessStr);
1498}
1499
The Android Open Source Project99409882009-03-18 22:20:24 -07001500
1501/*
1502 * Advance "ptr" to ensure 32-bit alignment.
1503 */
1504static inline const u1* align32(const u1* ptr)
1505{
1506 return (u1*) (((int) ptr + 3) & ~0x03);
1507}
1508
Andy McFadden10351272009-03-24 21:30:32 -07001509
1510/*
1511 * Dump a map in the "differential" format.
1512 *
1513 * TODO: show a hex dump of the compressed data. (We can show the
1514 * uncompressed data if we move the compression code to libdex; otherwise
1515 * it's too complex to merit a fast & fragile implementation here.)
1516 */
1517void dumpDifferentialCompressedMap(const u1** pData)
1518{
1519 const u1* data = *pData;
1520 const u1* dataStart = data -1; // format byte already removed
1521 u1 regWidth;
1522 u2 numEntries;
1523
1524 /* standard header */
1525 regWidth = *data++;
1526 numEntries = *data++;
1527 numEntries |= (*data++) << 8;
1528
1529 /* compressed data begins with the compressed data length */
1530 int compressedLen = readUnsignedLeb128(&data);
1531 int addrWidth = 1;
1532 if ((*data & 0x80) != 0)
1533 addrWidth++;
1534
1535 int origLen = 4 + (addrWidth + regWidth) * numEntries;
1536 int compLen = (data - dataStart) + compressedLen;
1537
1538 printf(" (differential compression %d -> %d [%d -> %d])\n",
1539 origLen, compLen,
1540 (addrWidth + regWidth) * numEntries, compressedLen);
1541
1542 /* skip past end of entry */
1543 data += compressedLen;
1544
1545 *pData = data;
1546}
1547
The Android Open Source Project99409882009-03-18 22:20:24 -07001548/*
1549 * Dump register map contents of the current method.
1550 *
1551 * "*pData" should point to the start of the register map data. Advances
1552 * "*pData" to the start of the next map.
1553 */
1554void dumpMethodMap(DexFile* pDexFile, const DexMethod* pDexMethod, int idx,
1555 const u1** pData)
1556{
1557 const u1* data = *pData;
1558 const DexMethodId* pMethodId;
1559 const char* name;
1560 int offset = data - (u1*) pDexFile->pOptHeader;
1561
1562 pMethodId = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
1563 name = dexStringById(pDexFile, pMethodId->nameIdx);
1564 printf(" #%d: 0x%08x %s\n", idx, offset, name);
1565
1566 u1 format;
1567 int addrWidth;
1568
1569 format = *data++;
1570 if (format == 1) { /* kRegMapFormatNone */
1571 /* no map */
1572 printf(" (no map)\n");
1573 addrWidth = 0;
1574 } else if (format == 2) { /* kRegMapFormatCompact8 */
1575 addrWidth = 1;
1576 } else if (format == 3) { /* kRegMapFormatCompact16 */
1577 addrWidth = 2;
Andy McFadden10351272009-03-24 21:30:32 -07001578 } else if (format == 4) { /* kRegMapFormatDifferential */
1579 dumpDifferentialCompressedMap(&data);
1580 goto bail;
The Android Open Source Project99409882009-03-18 22:20:24 -07001581 } else {
1582 printf(" (unknown format %d!)\n", format);
Andy McFadden10351272009-03-24 21:30:32 -07001583 /* don't know how to skip data; failure will cascade to end of class */
1584 goto bail;
The Android Open Source Project99409882009-03-18 22:20:24 -07001585 }
1586
1587 if (addrWidth > 0) {
1588 u1 regWidth;
1589 u2 numEntries;
1590 int idx, addr, byte;
1591
1592 regWidth = *data++;
1593 numEntries = *data++;
1594 numEntries |= (*data++) << 8;
1595
1596 for (idx = 0; idx < numEntries; idx++) {
1597 addr = *data++;
1598 if (addrWidth > 1)
1599 addr |= (*data++) << 8;
1600
1601 printf(" %4x:", addr);
1602 for (byte = 0; byte < regWidth; byte++) {
1603 printf(" %02x", *data++);
1604 }
1605 printf("\n");
1606 }
1607 }
1608
Andy McFadden10351272009-03-24 21:30:32 -07001609bail:
The Android Open Source Project99409882009-03-18 22:20:24 -07001610 //if (addrWidth >= 0)
1611 // *pData = align32(data);
1612 *pData = data;
1613}
1614
1615/*
1616 * Dump the contents of the register map area.
1617 *
1618 * These are only present in optimized DEX files, and the structure is
1619 * not really exposed to other parts of the VM itself. We're going to
1620 * dig through them here, but this is pretty fragile. DO NOT rely on
1621 * this or derive other code from it.
1622 */
1623void dumpRegisterMaps(DexFile* pDexFile)
1624{
1625 const u1* pClassPool = pDexFile->pRegisterMapPool;
1626 const u4* classOffsets;
1627 const u1* ptr;
1628 u4 numClasses;
1629 int baseFileOffset = (u1*) pClassPool - (u1*) pDexFile->pOptHeader;
1630 int idx;
1631
1632 if (pClassPool == NULL) {
1633 printf("No register maps found\n");
1634 return;
1635 }
1636
1637 ptr = pClassPool;
1638 numClasses = get4LE(ptr);
1639 ptr += sizeof(u4);
1640 classOffsets = (const u4*) ptr;
1641
1642 printf("RMAP begins at offset 0x%07x\n", baseFileOffset);
1643 printf("Maps for %d classes\n", numClasses);
1644 for (idx = 0; idx < (int) numClasses; idx++) {
1645 const DexClassDef* pClassDef;
1646 const char* classDescriptor;
1647
1648 pClassDef = dexGetClassDef(pDexFile, idx);
1649 classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
1650
1651 printf("%4d: +%d (0x%08x) %s\n", idx, classOffsets[idx],
1652 baseFileOffset + classOffsets[idx], classDescriptor);
1653
1654 if (classOffsets[idx] == 0)
1655 continue;
1656
1657 /*
1658 * What follows is a series of RegisterMap entries, one for every
1659 * direct method, then one for every virtual method.
1660 */
1661 DexClassData* pClassData;
1662 const u1* pEncodedData;
1663 const u1* data = (u1*) pClassPool + classOffsets[idx];
1664 u2 methodCount;
1665 int i;
1666
1667 pEncodedData = dexGetClassData(pDexFile, pClassDef);
1668 pClassData = dexReadAndVerifyClassData(&pEncodedData, NULL);
1669 if (pClassData == NULL) {
1670 fprintf(stderr, "Trouble reading class data\n");
1671 continue;
1672 }
1673
1674 methodCount = *data++;
1675 methodCount |= (*data++) << 8;
1676 data += 2; /* two pad bytes follow methodCount */
1677 if (methodCount != pClassData->header.directMethodsSize
1678 + pClassData->header.virtualMethodsSize)
1679 {
1680 printf("NOTE: method count discrepancy (%d != %d + %d)\n",
1681 methodCount, pClassData->header.directMethodsSize,
1682 pClassData->header.virtualMethodsSize);
1683 /* this is bad, but keep going anyway */
1684 }
1685
1686 printf(" direct methods: %d\n",
1687 pClassData->header.directMethodsSize);
1688 for (i = 0; i < (int) pClassData->header.directMethodsSize; i++) {
1689 dumpMethodMap(pDexFile, &pClassData->directMethods[i], i, &data);
1690 }
1691
1692 printf(" virtual methods: %d\n",
1693 pClassData->header.virtualMethodsSize);
1694 for (i = 0; i < (int) pClassData->header.virtualMethodsSize; i++) {
1695 dumpMethodMap(pDexFile, &pClassData->virtualMethods[i], i, &data);
1696 }
1697
1698 free(pClassData);
1699 }
1700}
1701
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001702/*
1703 * Dump the requested sections of the file.
1704 */
1705void processDexFile(const char* fileName, DexFile* pDexFile)
1706{
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001707 char* package = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001708 int i;
1709
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001710 if (gOptions.verbose) {
1711 printf("Opened '%s', DEX version '%.3s'\n", fileName,
1712 pDexFile->pHeader->magic +4);
1713 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001714
The Android Open Source Project99409882009-03-18 22:20:24 -07001715 if (gOptions.dumpRegisterMaps) {
1716 dumpRegisterMaps(pDexFile);
1717 return;
1718 }
1719
Andy McFadden0ea77b92010-04-20 14:18:59 -07001720 if (gOptions.showFileHeaders) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001721 dumpFileHeader(pDexFile);
Andy McFadden0ea77b92010-04-20 14:18:59 -07001722 dumpAuxDirectory(pDexFile);
1723 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001724
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001725 if (gOptions.outputFormat == OUTPUT_XML)
1726 printf("<api>\n");
1727
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001728 for (i = 0; i < (int) pDexFile->pHeader->classDefsSize; i++) {
1729 if (gOptions.showSectionHeaders)
1730 dumpClassDef(pDexFile, i);
1731
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001732 dumpClass(pDexFile, i, &package);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001733 }
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001734
1735 /* free the last one allocated */
1736 if (package != NULL) {
1737 printf("</package>\n");
1738 free(package);
1739 }
1740
1741 if (gOptions.outputFormat == OUTPUT_XML)
1742 printf("</api>\n");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001743}
1744
1745
1746/*
1747 * Process one file.
1748 */
1749int process(const char* fileName)
1750{
1751 DexFile* pDexFile = NULL;
1752 MemMapping map;
1753 bool mapped = false;
1754 int result = -1;
1755
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001756 if (gOptions.verbose)
1757 printf("Processing '%s'...\n", fileName);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001758
1759 if (dexOpenAndMap(fileName, gOptions.tempFileName, &map, false) != 0)
1760 goto bail;
1761 mapped = true;
1762
Andy McFadden2124cb82009-03-25 15:37:39 -07001763 int flags = kDexParseVerifyChecksum;
1764 if (gOptions.ignoreBadChecksum)
1765 flags |= kDexParseContinueOnError;
1766
1767 pDexFile = dexFileParse(map.addr, map.length, flags);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001768 if (pDexFile == NULL) {
1769 fprintf(stderr, "ERROR: DEX parse failed\n");
1770 goto bail;
1771 }
1772
Andy McFadden0198b142009-04-02 14:48:56 -07001773 if (gOptions.checksumOnly) {
1774 printf("Checksum verified\n");
1775 } else {
1776 processDexFile(fileName, pDexFile);
1777 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001778
1779 result = 0;
1780
1781bail:
1782 if (mapped)
1783 sysReleaseShmem(&map);
1784 if (pDexFile != NULL)
1785 dexFileFree(pDexFile);
1786 return result;
1787}
1788
1789
1790/*
1791 * Show usage.
1792 */
1793void usage(void)
1794{
1795 fprintf(stderr, "Copyright (C) 2007 The Android Open Source Project\n\n");
Andy McFadden0198b142009-04-02 14:48:56 -07001796 fprintf(stderr,
Andy McFaddend18aff32009-05-06 10:19:16 -07001797 "%s: [-c] [-d] [-f] [-h] [-i] [-l layout] [-m] [-t tempfile] dexfile...\n",
The Android Open Source Project99409882009-03-18 22:20:24 -07001798 gProgName);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001799 fprintf(stderr, "\n");
Andy McFadden0198b142009-04-02 14:48:56 -07001800 fprintf(stderr, " -c : verify checksum and exit\n");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001801 fprintf(stderr, " -d : disassemble code sections\n");
1802 fprintf(stderr, " -f : display summary information from file header\n");
1803 fprintf(stderr, " -h : display file header details\n");
Andy McFadden2124cb82009-03-25 15:37:39 -07001804 fprintf(stderr, " -i : ignore checksum failures\n");
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001805 fprintf(stderr, " -l : output layout, either 'plain' or 'xml'\n");
The Android Open Source Project99409882009-03-18 22:20:24 -07001806 fprintf(stderr, " -m : dump register maps (and nothing else)\n");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001807 fprintf(stderr, " -t : temp file name (defaults to /sdcard/dex-temp-*)\n");
1808}
1809
1810/*
1811 * Parse args.
1812 *
1813 * I'm not using getopt_long() because we may not have it in libc.
1814 */
1815int main(int argc, char* const argv[])
1816{
1817 bool wantUsage = false;
1818 int ic;
1819
1820 memset(&gOptions, 0, sizeof(gOptions));
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001821 gOptions.verbose = true;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001822
1823 while (1) {
Andy McFaddend18aff32009-05-06 10:19:16 -07001824 ic = getopt(argc, argv, "cdfhil:mt:");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001825 if (ic < 0)
1826 break;
1827
1828 switch (ic) {
Andy McFadden0198b142009-04-02 14:48:56 -07001829 case 'c': // verify the checksum then exit
1830 gOptions.checksumOnly = true;
1831 break;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001832 case 'd': // disassemble Dalvik instructions
1833 gOptions.disassemble = true;
1834 break;
1835 case 'f': // dump outer file header
1836 gOptions.showFileHeaders = true;
1837 break;
1838 case 'h': // dump section headers, i.e. all meta-data
1839 gOptions.showSectionHeaders = true;
1840 break;
Andy McFadden2124cb82009-03-25 15:37:39 -07001841 case 'i': // continue even if checksum is bad
1842 gOptions.ignoreBadChecksum = true;
1843 break;
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001844 case 'l': // layout
1845 if (strcmp(optarg, "plain") == 0) {
1846 gOptions.outputFormat = OUTPUT_PLAIN;
1847 } else if (strcmp(optarg, "xml") == 0) {
1848 gOptions.outputFormat = OUTPUT_XML;
1849 gOptions.verbose = false;
1850 gOptions.exportsOnly = true;
1851 } else {
1852 wantUsage = true;
1853 }
1854 break;
The Android Open Source Project99409882009-03-18 22:20:24 -07001855 case 'm': // dump register maps only
1856 gOptions.dumpRegisterMaps = true;
1857 break;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001858 case 't': // temp file, used when opening compressed Jar
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001859 gOptions.tempFileName = optarg;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001860 break;
1861 default:
1862 wantUsage = true;
1863 break;
1864 }
1865 }
1866
1867 if (optind == argc) {
1868 fprintf(stderr, "%s: no file specified\n", gProgName);
1869 wantUsage = true;
1870 }
1871
Andy McFadden0198b142009-04-02 14:48:56 -07001872 if (gOptions.checksumOnly && gOptions.ignoreBadChecksum) {
1873 fprintf(stderr, "Can't specify both -c and -i\n");
1874 wantUsage = true;
1875 }
1876
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001877 /* initialize some VM tables */
1878 gInstrWidth = dexCreateInstrWidthTable();
1879 gInstrFormat = dexCreateInstrFormatTable();
1880
1881 if (wantUsage) {
1882 usage();
1883 return 2;
1884 }
1885
Andy McFadden0198b142009-04-02 14:48:56 -07001886 int result = 0;
1887 while (optind < argc) {
1888 result |= process(argv[optind++]);
1889 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001890
1891 free(gInstrWidth);
1892 free(gInstrFormat);
1893
Andy McFadden0198b142009-04-02 14:48:56 -07001894 return (result != 0);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001895}