blob: 636408afbd758e7dd467d978126f340bab178c43 [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"
Andy McFaddenc6b25c72010-06-22 11:01:20 -070037#include "libdex/OpCodeNames.h"
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080038#include "libdex/SysUtil.h"
39#include "libdex/CmdUtils.h"
40
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080041#include <stdlib.h>
42#include <stdio.h>
43#include <fcntl.h>
44#include <string.h>
45#include <unistd.h>
46#include <getopt.h>
47#include <errno.h>
48#include <assert.h>
49
50static const char* gProgName = "dexdump";
51
52static InstructionWidth* gInstrWidth;
53static InstructionFormat* gInstrFormat;
54
Andy McFaddena2ee53b2009-05-05 16:52:10 -070055typedef enum OutputFormat {
56 OUTPUT_PLAIN = 0, /* default */
57 OUTPUT_XML, /* fancy */
58} OutputFormat;
59
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080060/* command-line options */
61struct {
Andy McFadden0198b142009-04-02 14:48:56 -070062 bool checksumOnly;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080063 bool disassemble;
64 bool showFileHeaders;
65 bool showSectionHeaders;
Andy McFadden2124cb82009-03-25 15:37:39 -070066 bool ignoreBadChecksum;
The Android Open Source Project99409882009-03-18 22:20:24 -070067 bool dumpRegisterMaps;
Andy McFaddena2ee53b2009-05-05 16:52:10 -070068 OutputFormat outputFormat;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080069 const char* tempFileName;
Andy McFaddena2ee53b2009-05-05 16:52:10 -070070 bool exportsOnly;
71 bool verbose;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080072} gOptions;
73
74/* basic info about a field or method */
75typedef struct FieldMethodInfo {
76 const char* classDescriptor;
77 const char* name;
78 const char* signature;
79} FieldMethodInfo;
80
81/*
Carl Shapirode750892010-06-08 16:37:12 -070082 * Get 2 little-endian bytes.
83 */
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080084static inline u2 get2LE(unsigned char const* pSrc)
85{
86 return pSrc[0] | (pSrc[1] << 8);
Carl Shapirode750892010-06-08 16:37:12 -070087}
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080088
89/*
Carl Shapirode750892010-06-08 16:37:12 -070090 * Get 4 little-endian bytes.
91 */
The Android Open Source Project99409882009-03-18 22:20:24 -070092static inline u4 get4LE(unsigned char const* pSrc)
93{
94 return pSrc[0] | (pSrc[1] << 8) | (pSrc[2] << 16) | (pSrc[3] << 24);
Carl Shapirode750892010-06-08 16:37:12 -070095}
The Android Open Source Project99409882009-03-18 22:20:24 -070096
97/*
Andy McFaddena2ee53b2009-05-05 16:52:10 -070098 * Converts a single-character primitive type into its human-readable
99 * equivalent.
100 */
101static const char* primitiveTypeLabel(char typeChar)
102{
103 switch (typeChar) {
104 case 'B': return "byte";
105 case 'C': return "char";
106 case 'D': return "double";
107 case 'F': return "float";
108 case 'I': return "int";
109 case 'J': return "long";
110 case 'S': return "short";
111 case 'V': return "void";
112 case 'Z': return "boolean";
113 default:
114 return "UNKNOWN";
115 }
116}
117
118/*
119 * Converts a type descriptor to human-readable "dotted" form. For
120 * example, "Ljava/lang/String;" becomes "java.lang.String", and
121 * "[I" becomes "int[]". Also converts '$' to '.', which means this
122 * form can't be converted back to a descriptor.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800123 */
124static char* descriptorToDot(const char* str)
125{
Andy McFaddena2ee53b2009-05-05 16:52:10 -0700126 int targetLen = strlen(str);
127 int offset = 0;
128 int arrayDepth = 0;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800129 char* newStr;
130
Andy McFaddena2ee53b2009-05-05 16:52:10 -0700131 /* strip leading [s; will be added to end */
132 while (targetLen > 1 && str[offset] == '[') {
133 offset++;
134 targetLen--;
135 }
136 arrayDepth = offset;
137
138 if (targetLen == 1) {
139 /* primitive type */
140 str = primitiveTypeLabel(str[offset]);
141 offset = 0;
142 targetLen = strlen(str);
143 } else {
144 /* account for leading 'L' and trailing ';' */
145 if (targetLen >= 2 && str[offset] == 'L' &&
146 str[offset+targetLen-1] == ';')
147 {
148 targetLen -= 2;
149 offset++;
150 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800151 }
152
Andy McFaddena2ee53b2009-05-05 16:52:10 -0700153 newStr = malloc(targetLen + arrayDepth * 2 +1);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800154
Andy McFaddena2ee53b2009-05-05 16:52:10 -0700155 /* copy class name over */
156 int i;
157 for (i = 0; i < targetLen; i++) {
158 char ch = str[offset + i];
159 newStr[i] = (ch == '/' || ch == '$') ? '.' : ch;
160 }
161
162 /* add the appropriate number of brackets for arrays */
163 while (arrayDepth-- > 0) {
164 newStr[i++] = '[';
165 newStr[i++] = ']';
166 }
167 newStr[i] = '\0';
168 assert(i == targetLen + arrayDepth * 2);
169
170 return newStr;
171}
172
173/*
174 * Converts the class name portion of a type descriptor to human-readable
175 * "dotted" form.
176 *
177 * Returns a newly-allocated string.
178 */
179static char* descriptorClassToDot(const char* str)
180{
181 const char* lastSlash;
182 char* newStr;
183 char* cp;
184
185 /* reduce to just the class name, trimming trailing ';' */
186 lastSlash = strrchr(str, '/');
187 if (lastSlash == NULL)
188 lastSlash = str + 1; /* start past 'L' */
189 else
190 lastSlash++; /* start past '/' */
191
192 newStr = strdup(lastSlash);
193 newStr[strlen(lastSlash)-1] = '\0';
194 for (cp = newStr; *cp != '\0'; cp++) {
195 if (*cp == '$')
196 *cp = '.';
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800197 }
198
199 return newStr;
200}
201
202/*
Andy McFaddena2ee53b2009-05-05 16:52:10 -0700203 * Returns a quoted string representing the boolean value.
204 */
205static const char* quotedBool(bool val)
206{
207 if (val)
208 return "\"true\"";
209 else
210 return "\"false\"";
211}
212
213static const char* quotedVisibility(u4 accessFlags)
214{
215 if ((accessFlags & ACC_PUBLIC) != 0)
216 return "\"public\"";
217 else if ((accessFlags & ACC_PROTECTED) != 0)
218 return "\"protected\"";
219 else if ((accessFlags & ACC_PRIVATE) != 0)
220 return "\"private\"";
221 else
222 return "\"package\"";
223}
224
225/*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800226 * Count the number of '1' bits in a word.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800227 */
228static int countOnes(u4 val)
229{
230 int count = 0;
231
Cosmin Cojocare3393432010-04-18 18:25:06 +0200232 val = val - ((val >> 1) & 0x55555555);
233 val = (val & 0x33333333) + ((val >> 2) & 0x33333333);
234 count = (((val + (val >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800235
236 return count;
237}
238
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800239/*
240 * Flag for use with createAccessFlagStr().
241 */
242typedef enum AccessFor {
243 kAccessForClass = 0, kAccessForMethod = 1, kAccessForField = 2,
244 kAccessForMAX
245} AccessFor;
246
247/*
248 * Create a new string with human-readable access flags.
249 *
250 * In the base language the access_flags fields are type u2; in Dalvik
251 * they're u4.
252 */
253static char* createAccessFlagStr(u4 flags, AccessFor forWhat)
254{
255#define NUM_FLAGS 18
256 static const char* kAccessStrings[kAccessForMAX][NUM_FLAGS] = {
Carl Shapirode750892010-06-08 16:37:12 -0700257 {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800258 /* class, inner class */
259 "PUBLIC", /* 0x0001 */
260 "PRIVATE", /* 0x0002 */
261 "PROTECTED", /* 0x0004 */
262 "STATIC", /* 0x0008 */
263 "FINAL", /* 0x0010 */
264 "?", /* 0x0020 */
265 "?", /* 0x0040 */
266 "?", /* 0x0080 */
267 "?", /* 0x0100 */
268 "INTERFACE", /* 0x0200 */
269 "ABSTRACT", /* 0x0400 */
270 "?", /* 0x0800 */
271 "SYNTHETIC", /* 0x1000 */
272 "ANNOTATION", /* 0x2000 */
273 "ENUM", /* 0x4000 */
274 "?", /* 0x8000 */
275 "VERIFIED", /* 0x10000 */
276 "OPTIMIZED", /* 0x20000 */
277 },
278 {
279 /* method */
280 "PUBLIC", /* 0x0001 */
281 "PRIVATE", /* 0x0002 */
282 "PROTECTED", /* 0x0004 */
283 "STATIC", /* 0x0008 */
284 "FINAL", /* 0x0010 */
285 "SYNCHRONIZED", /* 0x0020 */
286 "BRIDGE", /* 0x0040 */
287 "VARARGS", /* 0x0080 */
288 "NATIVE", /* 0x0100 */
289 "?", /* 0x0200 */
290 "ABSTRACT", /* 0x0400 */
291 "STRICT", /* 0x0800 */
292 "SYNTHETIC", /* 0x1000 */
293 "?", /* 0x2000 */
294 "?", /* 0x4000 */
295 "MIRANDA", /* 0x8000 */
296 "CONSTRUCTOR", /* 0x10000 */
297 "DECLARED_SYNCHRONIZED", /* 0x20000 */
298 },
299 {
300 /* field */
301 "PUBLIC", /* 0x0001 */
302 "PRIVATE", /* 0x0002 */
303 "PROTECTED", /* 0x0004 */
304 "STATIC", /* 0x0008 */
305 "FINAL", /* 0x0010 */
306 "?", /* 0x0020 */
307 "VOLATILE", /* 0x0040 */
308 "TRANSIENT", /* 0x0080 */
309 "?", /* 0x0100 */
310 "?", /* 0x0200 */
311 "?", /* 0x0400 */
312 "?", /* 0x0800 */
313 "SYNTHETIC", /* 0x1000 */
314 "?", /* 0x2000 */
315 "ENUM", /* 0x4000 */
316 "?", /* 0x8000 */
317 "?", /* 0x10000 */
318 "?", /* 0x20000 */
319 },
320 };
321 const int kLongest = 21; /* strlen of longest string above */
322 int i, count;
323 char* str;
324 char* cp;
325
326 /*
327 * Allocate enough storage to hold the expected number of strings,
328 * plus a space between each. We over-allocate, using the longest
329 * string above as the base metric.
330 */
331 count = countOnes(flags);
332 cp = str = (char*) malloc(count * (kLongest+1) +1);
333
334 for (i = 0; i < NUM_FLAGS; i++) {
335 if (flags & 0x01) {
336 const char* accessStr = kAccessStrings[forWhat][i];
337 int len = strlen(accessStr);
338 if (cp != str)
339 *cp++ = ' ';
340
341 memcpy(cp, accessStr, len);
342 cp += len;
343 }
344 flags >>= 1;
345 }
346 *cp = '\0';
347
348 return str;
349}
350
351
352/*
Andy McFadden0ea77b92010-04-20 14:18:59 -0700353 * Copy character data from "data" to "out", converting non-ASCII values
354 * to printf format chars or an ASCII filler ('.' or '?').
355 *
356 * The output buffer must be able to hold (2*len)+1 bytes. The result is
357 * NUL-terminated.
358 */
359static void asciify(char* out, const unsigned char* data, size_t len)
360{
361 while (len--) {
362 if (*data < 0x20) {
363 /* could do more here, but we don't need them yet */
364 switch (*data) {
365 case '\0':
366 *out++ = '\\';
367 *out++ = '0';
368 break;
369 case '\n':
370 *out++ = '\\';
371 *out++ = 'n';
372 break;
373 default:
374 *out++ = '.';
375 break;
376 }
377 } else if (*data >= 0x80) {
378 *out++ = '?';
379 } else {
380 *out++ = *data;
381 }
382 data++;
383 }
384 *out = '\0';
385}
386
387/*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800388 * Dump the file header.
389 */
390void dumpFileHeader(const DexFile* pDexFile)
391{
Andy McFadden0ea77b92010-04-20 14:18:59 -0700392 const DexOptHeader* pOptHeader = pDexFile->pOptHeader;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800393 const DexHeader* pHeader = pDexFile->pHeader;
Andy McFadden0ea77b92010-04-20 14:18:59 -0700394 char sanitized[sizeof(pHeader->magic)*2 +1];
395
396 assert(sizeof(pHeader->magic) == sizeof(pOptHeader->magic));
397
398 if (pOptHeader != NULL) {
399 printf("Optimized DEX file header:\n");
400
401 asciify(sanitized, pOptHeader->magic, sizeof(pOptHeader->magic));
402 printf("magic : '%s'\n", sanitized);
403 printf("dex_offset : %d (0x%06x)\n",
404 pOptHeader->dexOffset, pOptHeader->dexOffset);
405 printf("dex_length : %d\n", pOptHeader->dexLength);
406 printf("deps_offset : %d (0x%06x)\n",
407 pOptHeader->depsOffset, pOptHeader->depsOffset);
408 printf("deps_length : %d\n", pOptHeader->depsLength);
Dan Bornsteine377ef62010-08-31 16:50:00 -0700409 printf("opt_offset : %d (0x%06x)\n",
410 pOptHeader->optOffset, pOptHeader->optOffset);
411 printf("opt_length : %d\n", pOptHeader->optLength);
Andy McFadden0ea77b92010-04-20 14:18:59 -0700412 printf("flags : %08x\n", pOptHeader->flags);
413 printf("checksum : %08x\n", pOptHeader->checksum);
414 printf("\n");
415 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800416
417 printf("DEX file header:\n");
Andy McFadden0ea77b92010-04-20 14:18:59 -0700418 asciify(sanitized, pHeader->magic, sizeof(pHeader->magic));
419 printf("magic : '%s'\n", sanitized);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800420 printf("checksum : %08x\n", pHeader->checksum);
421 printf("signature : %02x%02x...%02x%02x\n",
422 pHeader->signature[0], pHeader->signature[1],
423 pHeader->signature[kSHA1DigestLen-2],
424 pHeader->signature[kSHA1DigestLen-1]);
425 printf("file_size : %d\n", pHeader->fileSize);
426 printf("header_size : %d\n", pHeader->headerSize);
427 printf("link_size : %d\n", pHeader->linkSize);
428 printf("link_off : %d (0x%06x)\n",
429 pHeader->linkOff, pHeader->linkOff);
430 printf("string_ids_size : %d\n", pHeader->stringIdsSize);
431 printf("string_ids_off : %d (0x%06x)\n",
432 pHeader->stringIdsOff, pHeader->stringIdsOff);
433 printf("type_ids_size : %d\n", pHeader->typeIdsSize);
434 printf("type_ids_off : %d (0x%06x)\n",
435 pHeader->typeIdsOff, pHeader->typeIdsOff);
436 printf("field_ids_size : %d\n", pHeader->fieldIdsSize);
437 printf("field_ids_off : %d (0x%06x)\n",
438 pHeader->fieldIdsOff, pHeader->fieldIdsOff);
439 printf("method_ids_size : %d\n", pHeader->methodIdsSize);
440 printf("method_ids_off : %d (0x%06x)\n",
441 pHeader->methodIdsOff, pHeader->methodIdsOff);
442 printf("class_defs_size : %d\n", pHeader->classDefsSize);
443 printf("class_defs_off : %d (0x%06x)\n",
444 pHeader->classDefsOff, pHeader->classDefsOff);
445 printf("data_size : %d\n", pHeader->dataSize);
446 printf("data_off : %d (0x%06x)\n",
447 pHeader->dataOff, pHeader->dataOff);
448 printf("\n");
449}
450
451/*
Dan Bornsteine377ef62010-08-31 16:50:00 -0700452 * Dump the "table of contents" for the opt area.
Andy McFadden0ea77b92010-04-20 14:18:59 -0700453 */
Dan Bornsteine377ef62010-08-31 16:50:00 -0700454void dumpOptDirectory(const DexFile* pDexFile)
Andy McFadden0ea77b92010-04-20 14:18:59 -0700455{
456 const DexOptHeader* pOptHeader = pDexFile->pOptHeader;
457 if (pOptHeader == NULL)
458 return;
459
Dan Bornsteine377ef62010-08-31 16:50:00 -0700460 printf("OPT section contents:\n");
Andy McFadden0ea77b92010-04-20 14:18:59 -0700461
Dan Bornsteine377ef62010-08-31 16:50:00 -0700462 const u4* pOpt = (const u4*) ((u1*) pOptHeader + pOptHeader->optOffset);
Andy McFadden0ea77b92010-04-20 14:18:59 -0700463
Dan Bornsteine377ef62010-08-31 16:50:00 -0700464 if (*pOpt == 0) {
Andy McFadden0ea77b92010-04-20 14:18:59 -0700465 printf("(1.0 format, only class lookup table is present)\n\n");
466 return;
467 }
468
469 /*
Dan Bornsteine377ef62010-08-31 16:50:00 -0700470 * The "opt" section is in "chunk" format: a 32-bit identifier, a 32-bit
Andy McFadden0ea77b92010-04-20 14:18:59 -0700471 * length, then the data. Chunks start on 64-bit boundaries.
472 */
Dan Bornsteine377ef62010-08-31 16:50:00 -0700473 while (*pOpt != kDexChunkEnd) {
Andy McFadden0ea77b92010-04-20 14:18:59 -0700474 const char* verboseStr;
475
Dan Bornsteine377ef62010-08-31 16:50:00 -0700476 u4 size = *(pOpt+1);
Andy McFadden0ea77b92010-04-20 14:18:59 -0700477
Dan Bornsteine377ef62010-08-31 16:50:00 -0700478 switch (*pOpt) {
Andy McFadden0ea77b92010-04-20 14:18:59 -0700479 case kDexChunkClassLookup:
480 verboseStr = "class lookup hash table";
481 break;
482 case kDexChunkRegisterMaps:
483 verboseStr = "register maps";
484 break;
Andy McFadden0ea77b92010-04-20 14:18:59 -0700485 default:
486 verboseStr = "(unknown chunk type)";
487 break;
488 }
489
Dan Bornsteine377ef62010-08-31 16:50:00 -0700490 printf("Chunk %08x (%c%c%c%c) - %s (%d bytes)\n", *pOpt,
491 *pOpt >> 24, (char)(*pOpt >> 16), (char)(*pOpt >> 8), (char)*pOpt,
Andy McFadden0ea77b92010-04-20 14:18:59 -0700492 verboseStr, size);
493
494 size = (size + 8 + 7) & ~7;
Dan Bornsteine377ef62010-08-31 16:50:00 -0700495 pOpt += size / sizeof(u4);
Andy McFadden0ea77b92010-04-20 14:18:59 -0700496 }
497 printf("\n");
498}
499
500/*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800501 * Dump a class_def_item.
502 */
503void dumpClassDef(DexFile* pDexFile, int idx)
504{
505 const DexClassDef* pClassDef;
506 const u1* pEncodedData;
507 DexClassData* pClassData;
508
509 pClassDef = dexGetClassDef(pDexFile, idx);
510 pEncodedData = dexGetClassData(pDexFile, pClassDef);
511 pClassData = dexReadAndVerifyClassData(&pEncodedData, NULL);
512
513 if (pClassData == NULL) {
514 fprintf(stderr, "Trouble reading class data\n");
515 return;
516 }
517
518 printf("Class #%d header:\n", idx);
519 printf("class_idx : %d\n", pClassDef->classIdx);
520 printf("access_flags : %d (0x%04x)\n",
521 pClassDef->accessFlags, pClassDef->accessFlags);
522 printf("superclass_idx : %d\n", pClassDef->superclassIdx);
523 printf("interfaces_off : %d (0x%06x)\n",
524 pClassDef->interfacesOff, pClassDef->interfacesOff);
525 printf("source_file_idx : %d\n", pClassDef->sourceFileIdx);
526 printf("annotations_off : %d (0x%06x)\n",
527 pClassDef->annotationsOff, pClassDef->annotationsOff);
528 printf("class_data_off : %d (0x%06x)\n",
529 pClassDef->classDataOff, pClassDef->classDataOff);
530 printf("static_fields_size : %d\n", pClassData->header.staticFieldsSize);
531 printf("instance_fields_size: %d\n",
532 pClassData->header.instanceFieldsSize);
533 printf("direct_methods_size : %d\n", pClassData->header.directMethodsSize);
534 printf("virtual_methods_size: %d\n",
535 pClassData->header.virtualMethodsSize);
536 printf("\n");
537
538 free(pClassData);
539}
540
541/*
Andy McFaddena2ee53b2009-05-05 16:52:10 -0700542 * Dump an interface that a class declares to implement.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800543 */
544void dumpInterface(const DexFile* pDexFile, const DexTypeItem* pTypeItem,
545 int i)
546{
547 const char* interfaceName =
548 dexStringByTypeIdx(pDexFile, pTypeItem->typeIdx);
549
Andy McFaddena2ee53b2009-05-05 16:52:10 -0700550 if (gOptions.outputFormat == OUTPUT_PLAIN) {
551 printf(" #%d : '%s'\n", i, interfaceName);
552 } else {
553 char* dotted = descriptorToDot(interfaceName);
554 printf("<implements name=\"%s\">\n</implements>\n", dotted);
555 free(dotted);
556 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800557}
558
559/*
560 * Dump the catches table associated with the code.
561 */
562void dumpCatches(DexFile* pDexFile, const DexCode* pCode)
563{
564 u4 triesSize = pCode->triesSize;
565
566 if (triesSize == 0) {
567 printf(" catches : (none)\n");
568 return;
Carl Shapirode750892010-06-08 16:37:12 -0700569 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800570
571 printf(" catches : %d\n", triesSize);
572
573 const DexTry* pTries = dexGetTries(pCode);
574 u4 i;
575
576 for (i = 0; i < triesSize; i++) {
577 const DexTry* pTry = &pTries[i];
578 u4 start = pTry->startAddr;
579 u4 end = start + pTry->insnCount;
580 DexCatchIterator iterator;
Carl Shapirode750892010-06-08 16:37:12 -0700581
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800582 printf(" 0x%04x - 0x%04x\n", start, end);
583
584 dexCatchIteratorInit(&iterator, pCode, pTry->handlerOff);
585
586 for (;;) {
587 DexCatchHandler* handler = dexCatchIteratorNext(&iterator);
588 const char* descriptor;
Carl Shapirode750892010-06-08 16:37:12 -0700589
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800590 if (handler == NULL) {
591 break;
592 }
Carl Shapirode750892010-06-08 16:37:12 -0700593
594 descriptor = (handler->typeIdx == kDexNoIndex) ? "<any>" :
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800595 dexStringByTypeIdx(pDexFile, handler->typeIdx);
Carl Shapirode750892010-06-08 16:37:12 -0700596
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800597 printf(" %s -> 0x%04x\n", descriptor,
598 handler->address);
599 }
600 }
601}
602
603static int dumpPositionsCb(void *cnxt, u4 address, u4 lineNum)
604{
605 printf(" 0x%04x line=%d\n", address, lineNum);
606 return 0;
607}
608
609/*
610 * Dump the positions list.
611 */
Carl Shapirode750892010-06-08 16:37:12 -0700612void dumpPositions(DexFile* pDexFile, const DexCode* pCode,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800613 const DexMethod *pDexMethod)
614{
615 printf(" positions : \n");
Carl Shapirode750892010-06-08 16:37:12 -0700616 const DexMethodId *pMethodId
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800617 = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
618 const char *classDescriptor
619 = dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
620
621 dexDecodeDebugInfo(pDexFile, pCode, classDescriptor, pMethodId->protoIdx,
622 pDexMethod->accessFlags, dumpPositionsCb, NULL, NULL);
623}
624
625static void dumpLocalsCb(void *cnxt, u2 reg, u4 startAddress,
626 u4 endAddress, const char *name, const char *descriptor,
627 const char *signature)
628{
629 printf(" 0x%04x - 0x%04x reg=%d %s %s %s\n",
Carl Shapirode750892010-06-08 16:37:12 -0700630 startAddress, endAddress, reg, name, descriptor,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800631 signature);
632}
633
634/*
635 * Dump the locals list.
636 */
637void dumpLocals(DexFile* pDexFile, const DexCode* pCode,
638 const DexMethod *pDexMethod)
639{
640 printf(" locals : \n");
641
Carl Shapirode750892010-06-08 16:37:12 -0700642 const DexMethodId *pMethodId
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800643 = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
Carl Shapirode750892010-06-08 16:37:12 -0700644 const char *classDescriptor
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800645 = dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
646
647 dexDecodeDebugInfo(pDexFile, pCode, classDescriptor, pMethodId->protoIdx,
648 pDexMethod->accessFlags, NULL, dumpLocalsCb, NULL);
649}
650
651/*
652 * Get information about a method.
653 */
654bool getMethodInfo(DexFile* pDexFile, u4 methodIdx, FieldMethodInfo* pMethInfo)
655{
656 const DexMethodId* pMethodId;
657
658 if (methodIdx >= pDexFile->pHeader->methodIdsSize)
659 return false;
660
661 pMethodId = dexGetMethodId(pDexFile, methodIdx);
662 pMethInfo->name = dexStringById(pDexFile, pMethodId->nameIdx);
663 pMethInfo->signature = dexCopyDescriptorFromMethodId(pDexFile, pMethodId);
664
Carl Shapirode750892010-06-08 16:37:12 -0700665 pMethInfo->classDescriptor =
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800666 dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
667 return true;
668}
669
670/*
671 * Get information about a field.
672 */
673bool getFieldInfo(DexFile* pDexFile, u4 fieldIdx, FieldMethodInfo* pFieldInfo)
674{
675 const DexFieldId* pFieldId;
676
677 if (fieldIdx >= pDexFile->pHeader->fieldIdsSize)
678 return false;
679
680 pFieldId = dexGetFieldId(pDexFile, fieldIdx);
681 pFieldInfo->name = dexStringById(pDexFile, pFieldId->nameIdx);
682 pFieldInfo->signature = dexStringByTypeIdx(pDexFile, pFieldId->typeIdx);
683 pFieldInfo->classDescriptor =
684 dexStringByTypeIdx(pDexFile, pFieldId->classIdx);
685 return true;
686}
687
688
689/*
690 * Look up a class' descriptor.
691 */
692const char* getClassDescriptor(DexFile* pDexFile, u4 classIdx)
693{
694 return dexStringByTypeIdx(pDexFile, classIdx);
695}
696
697/*
698 * Dump a single instruction.
699 */
700void dumpInstruction(DexFile* pDexFile, const DexCode* pCode, int insnIdx,
701 int insnWidth, const DecodedInstruction* pDecInsn)
702{
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800703 const u2* insns = pCode->insns;
704 int i;
705
706 printf("%06x:", ((u1*)insns - pDexFile->baseAddr) + insnIdx*2);
707 for (i = 0; i < 8; i++) {
708 if (i < insnWidth) {
709 if (i == 7) {
710 printf(" ... ");
711 } else {
712 /* print 16-bit value in little-endian order */
713 const u1* bytePtr = (const u1*) &insns[insnIdx+i];
714 printf(" %02x%02x", bytePtr[0], bytePtr[1]);
715 }
716 } else {
717 fputs(" ", stdout);
718 }
719 }
720
721 if (pDecInsn->opCode == OP_NOP) {
722 u2 instr = get2LE((const u1*) &insns[insnIdx]);
723 if (instr == kPackedSwitchSignature) {
724 printf("|%04x: packed-switch-data (%d units)",
725 insnIdx, insnWidth);
726 } else if (instr == kSparseSwitchSignature) {
727 printf("|%04x: sparse-switch-data (%d units)",
728 insnIdx, insnWidth);
729 } else if (instr == kArrayDataSignature) {
730 printf("|%04x: array-data (%d units)",
731 insnIdx, insnWidth);
732 } else {
733 printf("|%04x: nop // spacer", insnIdx);
734 }
735 } else {
Andy McFaddenc6b25c72010-06-22 11:01:20 -0700736 printf("|%04x: %s", insnIdx, dexGetOpcodeName(pDecInsn->opCode));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800737 }
738
739 switch (dexGetInstrFormat(gInstrFormat, pDecInsn->opCode)) {
740 case kFmt10x: // op
741 break;
742 case kFmt12x: // op vA, vB
743 printf(" v%d, v%d", pDecInsn->vA, pDecInsn->vB);
744 break;
745 case kFmt11n: // op vA, #+B
746 printf(" v%d, #int %d // #%x",
747 pDecInsn->vA, (s4)pDecInsn->vB, (u1)pDecInsn->vB);
748 break;
749 case kFmt11x: // op vAA
750 printf(" v%d", pDecInsn->vA);
751 break;
752 case kFmt10t: // op +AA
753 case kFmt20t: // op +AAAA
754 {
755 s4 targ = (s4) pDecInsn->vA;
756 printf(" %04x // %c%04x",
757 insnIdx + targ,
758 (targ < 0) ? '-' : '+',
759 (targ < 0) ? -targ : targ);
760 }
761 break;
762 case kFmt22x: // op vAA, vBBBB
763 printf(" v%d, v%d", pDecInsn->vA, pDecInsn->vB);
764 break;
765 case kFmt21t: // op vAA, +BBBB
766 {
767 s4 targ = (s4) pDecInsn->vB;
768 printf(" v%d, %04x // %c%04x", pDecInsn->vA,
769 insnIdx + targ,
770 (targ < 0) ? '-' : '+',
771 (targ < 0) ? -targ : targ);
772 }
773 break;
774 case kFmt21s: // op vAA, #+BBBB
775 printf(" v%d, #int %d // #%x",
776 pDecInsn->vA, (s4)pDecInsn->vB, (u2)pDecInsn->vB);
777 break;
778 case kFmt21h: // op vAA, #+BBBB0000[00000000]
779 // The printed format varies a bit based on the actual opcode.
780 if (pDecInsn->opCode == OP_CONST_HIGH16) {
781 s4 value = pDecInsn->vB << 16;
782 printf(" v%d, #int %d // #%x",
783 pDecInsn->vA, value, (u2)pDecInsn->vB);
784 } else {
785 s8 value = ((s8) pDecInsn->vB) << 48;
786 printf(" v%d, #long %lld // #%x",
787 pDecInsn->vA, value, (u2)pDecInsn->vB);
788 }
789 break;
790 case kFmt21c: // op vAA, thing@BBBB
791 if (pDecInsn->opCode == OP_CONST_STRING) {
792 printf(" v%d, \"%s\" // string@%04x", pDecInsn->vA,
793 dexStringById(pDexFile, pDecInsn->vB), pDecInsn->vB);
794 } else if (pDecInsn->opCode == OP_CHECK_CAST ||
795 pDecInsn->opCode == OP_NEW_INSTANCE ||
796 pDecInsn->opCode == OP_CONST_CLASS)
797 {
798 printf(" v%d, %s // class@%04x", pDecInsn->vA,
799 getClassDescriptor(pDexFile, pDecInsn->vB), pDecInsn->vB);
800 } else /* OP_SGET* */ {
801 FieldMethodInfo fieldInfo;
802 if (getFieldInfo(pDexFile, pDecInsn->vB, &fieldInfo)) {
803 printf(" v%d, %s.%s:%s // field@%04x", pDecInsn->vA,
804 fieldInfo.classDescriptor, fieldInfo.name,
805 fieldInfo.signature, pDecInsn->vB);
806 } else {
807 printf(" v%d, ??? // field@%04x", pDecInsn->vA, pDecInsn->vB);
808 }
809 }
810 break;
811 case kFmt23x: // op vAA, vBB, vCC
812 printf(" v%d, v%d, v%d", pDecInsn->vA, pDecInsn->vB, pDecInsn->vC);
813 break;
814 case kFmt22b: // op vAA, vBB, #+CC
815 printf(" v%d, v%d, #int %d // #%02x",
816 pDecInsn->vA, pDecInsn->vB, (s4)pDecInsn->vC, (u1)pDecInsn->vC);
817 break;
818 case kFmt22t: // op vA, vB, +CCCC
819 {
820 s4 targ = (s4) pDecInsn->vC;
821 printf(" v%d, v%d, %04x // %c%04x", pDecInsn->vA, pDecInsn->vB,
822 insnIdx + targ,
823 (targ < 0) ? '-' : '+',
824 (targ < 0) ? -targ : targ);
825 }
826 break;
827 case kFmt22s: // op vA, vB, #+CCCC
828 printf(" v%d, v%d, #int %d // #%04x",
829 pDecInsn->vA, pDecInsn->vB, (s4)pDecInsn->vC, (u2)pDecInsn->vC);
830 break;
831 case kFmt22c: // op vA, vB, thing@CCCC
Andy McFadden45a82a12010-06-22 10:34:21 -0700832 if (pDecInsn->opCode == OP_INSTANCE_OF ||
833 pDecInsn->opCode == OP_NEW_ARRAY)
834 {
835 printf(" v%d, v%d, %s // class@%04x",
836 pDecInsn->vA, pDecInsn->vB,
837 getClassDescriptor(pDexFile, pDecInsn->vC), pDecInsn->vC);
838 } else {
839 /* iget* and iput*, including dexopt-generated -volatile */
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800840 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 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800849 }
850 break;
851 case kFmt22cs: // [opt] op vA, vB, field offset CCCC
852 printf(" v%d, v%d, [obj+%04x]",
853 pDecInsn->vA, pDecInsn->vB, pDecInsn->vC);
854 break;
855 case kFmt30t:
856 printf(" #%08x", pDecInsn->vA);
857 break;
858 case kFmt31i: // op vAA, #+BBBBBBBB
859 {
860 /* this is often, but not always, a float */
861 union {
862 float f;
863 u4 i;
864 } conv;
865 conv.i = pDecInsn->vB;
866 printf(" v%d, #float %f // #%08x",
867 pDecInsn->vA, conv.f, pDecInsn->vB);
868 }
869 break;
870 case kFmt31c: // op vAA, thing@BBBBBBBB
871 printf(" v%d, \"%s\" // string@%08x", pDecInsn->vA,
872 dexStringById(pDexFile, pDecInsn->vB), pDecInsn->vB);
873 break;
874 case kFmt31t: // op vAA, offset +BBBBBBBB
875 printf(" v%d, %08x // +%08x",
876 pDecInsn->vA, insnIdx + pDecInsn->vB, pDecInsn->vB);
877 break;
878 case kFmt32x: // op vAAAA, vBBBB
879 printf(" v%d, v%d", pDecInsn->vA, pDecInsn->vB);
880 break;
881 case kFmt35c: // op vB, {vD, vE, vF, vG, vA}, thing@CCCC
882 {
883 /* NOTE: decoding of 35c doesn't quite match spec */
884 fputs(" {", stdout);
885 for (i = 0; i < (int) pDecInsn->vA; i++) {
886 if (i == 0)
887 printf("v%d", pDecInsn->arg[i]);
888 else
889 printf(", v%d", pDecInsn->arg[i]);
890 }
891 if (pDecInsn->opCode == OP_FILLED_NEW_ARRAY) {
892 printf("}, %s // class@%04x",
893 getClassDescriptor(pDexFile, pDecInsn->vB), pDecInsn->vB);
894 } else {
895 FieldMethodInfo methInfo;
896 if (getMethodInfo(pDexFile, pDecInsn->vB, &methInfo)) {
897 printf("}, %s.%s:%s // method@%04x",
898 methInfo.classDescriptor, methInfo.name,
899 methInfo.signature, pDecInsn->vB);
900 } else {
901 printf("}, ??? // method@%04x", pDecInsn->vB);
902 }
903 }
904 }
905 break;
906 case kFmt35ms: // [opt] invoke-virtual+super
907 case kFmt35fs: // [opt] invoke-interface
908 {
909 fputs(" {", stdout);
910 for (i = 0; i < (int) pDecInsn->vA; i++) {
911 if (i == 0)
912 printf("v%d", pDecInsn->arg[i]);
913 else
914 printf(", v%d", pDecInsn->arg[i]);
915 }
916 printf("}, [%04x] // vtable #%04x", pDecInsn->vB, pDecInsn->vB);
917 }
918 break;
919 case kFmt3rc: // op {vCCCC .. v(CCCC+AA-1)}, meth@BBBB
920 {
921 /*
922 * This doesn't match the "dx" output when some of the args are
923 * 64-bit values -- dx only shows the first register.
924 */
925 fputs(" {", stdout);
926 for (i = 0; i < (int) pDecInsn->vA; i++) {
927 if (i == 0)
928 printf("v%d", pDecInsn->vC + i);
929 else
930 printf(", v%d", pDecInsn->vC + i);
931 }
932 if (pDecInsn->opCode == OP_FILLED_NEW_ARRAY_RANGE) {
933 printf("}, %s // class@%04x",
934 getClassDescriptor(pDexFile, pDecInsn->vB), pDecInsn->vB);
935 } else {
936 FieldMethodInfo methInfo;
937 if (getMethodInfo(pDexFile, pDecInsn->vB, &methInfo)) {
938 printf("}, %s.%s:%s // method@%04x",
939 methInfo.classDescriptor, methInfo.name,
940 methInfo.signature, pDecInsn->vB);
941 } else {
942 printf("}, ??? // method@%04x", pDecInsn->vB);
943 }
944 }
945 }
946 break;
947 case kFmt3rms: // [opt] invoke-virtual+super/range
948 case kFmt3rfs: // [opt] invoke-interface/range
949 {
950 /*
951 * This doesn't match the "dx" output when some of the args are
952 * 64-bit values -- dx only shows the first register.
953 */
954 fputs(" {", stdout);
955 for (i = 0; i < (int) pDecInsn->vA; i++) {
956 if (i == 0)
957 printf("v%d", pDecInsn->vC + i);
958 else
959 printf(", v%d", pDecInsn->vC + i);
960 }
961 printf("}, [%04x] // vtable #%04x", pDecInsn->vB, pDecInsn->vB);
962 }
963 break;
Andy McFaddenb0a05412009-11-19 10:23:41 -0800964 case kFmt3rinline: // [opt] execute-inline/range
965 {
966 fputs(" {", stdout);
967 for (i = 0; i < (int) pDecInsn->vA; i++) {
968 if (i == 0)
969 printf("v%d", pDecInsn->vC + i);
970 else
971 printf(", v%d", pDecInsn->vC + i);
972 }
973 printf("}, [%04x] // inline #%04x", pDecInsn->vB, pDecInsn->vB);
974 }
975 break;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800976 case kFmt3inline: // [opt] inline invoke
977 {
978#if 0
979 const InlineOperation* inlineOpsTable = dvmGetInlineOpsTable();
980 u4 tableLen = dvmGetInlineOpsTableLength();
981#endif
982
983 fputs(" {", stdout);
984 for (i = 0; i < (int) pDecInsn->vA; i++) {
985 if (i == 0)
986 printf("v%d", pDecInsn->arg[i]);
987 else
988 printf(", v%d", pDecInsn->arg[i]);
989 }
990#if 0
991 if (pDecInsn->vB < tableLen) {
992 printf("}, %s.%s:%s // inline #%04x",
993 inlineOpsTable[pDecInsn->vB].classDescriptor,
994 inlineOpsTable[pDecInsn->vB].methodName,
995 inlineOpsTable[pDecInsn->vB].methodSignature,
996 pDecInsn->vB);
997 } else {
998#endif
999 printf("}, [%04x] // inline #%04x", pDecInsn->vB, pDecInsn->vB);
1000#if 0
1001 }
1002#endif
1003 }
1004 break;
1005 case kFmt51l: // op vAA, #+BBBBBBBBBBBBBBBB
1006 {
1007 /* this is often, but not always, a double */
1008 union {
1009 double d;
1010 u8 j;
1011 } conv;
1012 conv.j = pDecInsn->vB_wide;
1013 printf(" v%d, #double %f // #%016llx",
1014 pDecInsn->vA, conv.d, pDecInsn->vB_wide);
1015 }
1016 break;
1017 case kFmtUnknown:
1018 break;
1019 default:
1020 printf(" ???");
1021 break;
1022 }
1023
1024
1025 putchar('\n');
1026
1027}
1028
1029/*
1030 * Dump a bytecode disassembly.
1031 */
1032void dumpBytecodes(DexFile* pDexFile, const DexMethod* pDexMethod)
1033{
1034 const DexCode* pCode = dexGetCode(pDexFile, pDexMethod);
1035 const u2* insns;
1036 int insnIdx;
1037 FieldMethodInfo methInfo;
1038 int startAddr;
1039 char* className = NULL;
1040
1041 assert(pCode->insnsSize > 0);
1042 insns = pCode->insns;
1043
1044 getMethodInfo(pDexFile, pDexMethod->methodIdx, &methInfo);
1045 startAddr = ((u1*)pCode - pDexFile->baseAddr);
1046 className = descriptorToDot(methInfo.classDescriptor);
1047
1048 printf("%06x: |[%06x] %s.%s:%s\n",
1049 startAddr, startAddr,
1050 className, methInfo.name, methInfo.signature);
1051
1052 insnIdx = 0;
1053 while (insnIdx < (int) pCode->insnsSize) {
1054 int insnWidth;
1055 OpCode opCode;
1056 DecodedInstruction decInsn;
1057 u2 instr;
1058
1059 instr = get2LE((const u1*)insns);
1060 if (instr == kPackedSwitchSignature) {
1061 insnWidth = 4 + get2LE((const u1*)(insns+1)) * 2;
1062 } else if (instr == kSparseSwitchSignature) {
1063 insnWidth = 2 + get2LE((const u1*)(insns+1)) * 4;
1064 } else if (instr == kArrayDataSignature) {
1065 int width = get2LE((const u1*)(insns+1));
Carl Shapirode750892010-06-08 16:37:12 -07001066 int size = get2LE((const u1*)(insns+2)) |
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001067 (get2LE((const u1*)(insns+3))<<16);
Carl Shapirode750892010-06-08 16:37:12 -07001068 // The plus 1 is to round up for odd size and width
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001069 insnWidth = 4 + ((size * width) + 1) / 2;
1070 } else {
1071 opCode = instr & 0xff;
1072 insnWidth = dexGetInstrWidthAbs(gInstrWidth, opCode);
1073 if (insnWidth == 0) {
1074 fprintf(stderr,
1075 "GLITCH: zero-width instruction at idx=0x%04x\n", insnIdx);
1076 break;
1077 }
1078 }
1079
1080 dexDecodeInstruction(gInstrFormat, insns, &decInsn);
1081 dumpInstruction(pDexFile, pCode, insnIdx, insnWidth, &decInsn);
1082
1083 insns += insnWidth;
1084 insnIdx += insnWidth;
1085 }
1086
1087 free(className);
1088}
1089
1090/*
1091 * Dump a "code" struct.
1092 */
1093void dumpCode(DexFile* pDexFile, const DexMethod* pDexMethod)
1094{
1095 const DexCode* pCode = dexGetCode(pDexFile, pDexMethod);
1096
1097 printf(" registers : %d\n", pCode->registersSize);
1098 printf(" ins : %d\n", pCode->insSize);
1099 printf(" outs : %d\n", pCode->outsSize);
1100 printf(" insns size : %d 16-bit code units\n", pCode->insnsSize);
1101
1102 if (gOptions.disassemble)
1103 dumpBytecodes(pDexFile, pDexMethod);
1104
1105 dumpCatches(pDexFile, pCode);
1106 /* both of these are encoded in debug info */
1107 dumpPositions(pDexFile, pCode, pDexMethod);
1108 dumpLocals(pDexFile, pCode, pDexMethod);
1109}
1110
1111/*
1112 * Dump a method.
1113 */
1114void dumpMethod(DexFile* pDexFile, const DexMethod* pDexMethod, int i)
1115{
1116 const DexMethodId* pMethodId;
1117 const char* backDescriptor;
1118 const char* name;
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001119 char* typeDescriptor = NULL;
1120 char* accessStr = NULL;
1121
1122 if (gOptions.exportsOnly &&
1123 (pDexMethod->accessFlags & (ACC_PUBLIC | ACC_PROTECTED)) == 0)
1124 {
1125 return;
1126 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001127
1128 pMethodId = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
1129 name = dexStringById(pDexFile, pMethodId->nameIdx);
1130 typeDescriptor = dexCopyDescriptorFromMethodId(pDexFile, pMethodId);
1131
1132 backDescriptor = dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
1133
1134 accessStr = createAccessFlagStr(pDexMethod->accessFlags,
1135 kAccessForMethod);
1136
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001137 if (gOptions.outputFormat == OUTPUT_PLAIN) {
1138 printf(" #%d : (in %s)\n", i, backDescriptor);
1139 printf(" name : '%s'\n", name);
1140 printf(" type : '%s'\n", typeDescriptor);
1141 printf(" access : 0x%04x (%s)\n",
1142 pDexMethod->accessFlags, accessStr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001143
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001144 if (pDexMethod->codeOff == 0) {
1145 printf(" code : (none)\n");
1146 } else {
1147 printf(" code -\n");
1148 dumpCode(pDexFile, pDexMethod);
1149 }
1150
1151 if (gOptions.disassemble)
1152 putchar('\n');
1153 } else if (gOptions.outputFormat == OUTPUT_XML) {
1154 bool constructor = (name[0] == '<');
1155
1156 if (constructor) {
1157 char* tmp;
1158
1159 tmp = descriptorClassToDot(backDescriptor);
1160 printf("<constructor name=\"%s\"\n", tmp);
1161 free(tmp);
1162
1163 tmp = descriptorToDot(backDescriptor);
1164 printf(" type=\"%s\"\n", tmp);
1165 free(tmp);
1166 } else {
1167 printf("<method name=\"%s\"\n", name);
1168
1169 const char* returnType = strrchr(typeDescriptor, ')');
1170 if (returnType == NULL) {
1171 fprintf(stderr, "bad method type descriptor '%s'\n",
1172 typeDescriptor);
1173 goto bail;
1174 }
1175
1176 char* tmp = descriptorToDot(returnType+1);
1177 printf(" return=\"%s\"\n", tmp);
1178 free(tmp);
1179
1180 printf(" abstract=%s\n",
1181 quotedBool((pDexMethod->accessFlags & ACC_ABSTRACT) != 0));
1182 printf(" native=%s\n",
1183 quotedBool((pDexMethod->accessFlags & ACC_NATIVE) != 0));
1184
1185 bool isSync =
1186 (pDexMethod->accessFlags & ACC_SYNCHRONIZED) != 0 ||
1187 (pDexMethod->accessFlags & ACC_DECLARED_SYNCHRONIZED) != 0;
1188 printf(" synchronized=%s\n", quotedBool(isSync));
1189 }
1190
1191 printf(" static=%s\n",
1192 quotedBool((pDexMethod->accessFlags & ACC_STATIC) != 0));
1193 printf(" final=%s\n",
1194 quotedBool((pDexMethod->accessFlags & ACC_FINAL) != 0));
1195 // "deprecated=" not knowable w/o parsing annotations
1196 printf(" visibility=%s\n",
1197 quotedVisibility(pDexMethod->accessFlags));
1198
1199 printf(">\n");
1200
1201 /*
1202 * Parameters.
1203 */
1204 if (typeDescriptor[0] != '(') {
1205 fprintf(stderr, "ERROR: bad descriptor '%s'\n", typeDescriptor);
1206 goto bail;
1207 }
1208
1209 char tmpBuf[strlen(typeDescriptor)+1]; /* more than big enough */
1210 int argNum = 0;
1211
1212 const char* base = typeDescriptor+1;
1213
1214 while (*base != ')') {
1215 char* cp = tmpBuf;
1216
1217 while (*base == '[')
1218 *cp++ = *base++;
1219
1220 if (*base == 'L') {
1221 /* copy through ';' */
1222 do {
1223 *cp = *base++;
1224 } while (*cp++ != ';');
1225 } else {
1226 /* primitive char, copy it */
1227 if (strchr("ZBCSIFJD", *base) == NULL) {
1228 fprintf(stderr, "ERROR: bad method signature '%s'\n", base);
1229 goto bail;
1230 }
1231 *cp++ = *base++;
1232 }
1233
1234 /* null terminate and display */
1235 *cp++ = '\0';
1236
1237 char* tmp = descriptorToDot(tmpBuf);
1238 printf("<parameter name=\"arg%d\" type=\"%s\">\n</parameter>\n",
1239 argNum++, tmp);
1240 free(tmp);
1241 }
1242
1243 if (constructor)
1244 printf("</constructor>\n");
1245 else
1246 printf("</method>\n");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001247 }
1248
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001249bail:
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001250 free(typeDescriptor);
1251 free(accessStr);
1252}
1253
1254/*
1255 * Dump a static (class) field.
1256 */
1257void dumpSField(const DexFile* pDexFile, const DexField* pSField, int i)
1258{
1259 const DexFieldId* pFieldId;
1260 const char* backDescriptor;
1261 const char* name;
1262 const char* typeDescriptor;
1263 char* accessStr;
1264
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001265 if (gOptions.exportsOnly &&
1266 (pSField->accessFlags & (ACC_PUBLIC | ACC_PROTECTED)) == 0)
1267 {
1268 return;
1269 }
1270
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001271 pFieldId = dexGetFieldId(pDexFile, pSField->fieldIdx);
1272 name = dexStringById(pDexFile, pFieldId->nameIdx);
1273 typeDescriptor = dexStringByTypeIdx(pDexFile, pFieldId->typeIdx);
1274 backDescriptor = dexStringByTypeIdx(pDexFile, pFieldId->classIdx);
1275
1276 accessStr = createAccessFlagStr(pSField->accessFlags, kAccessForField);
1277
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001278 if (gOptions.outputFormat == OUTPUT_PLAIN) {
1279 printf(" #%d : (in %s)\n", i, backDescriptor);
1280 printf(" name : '%s'\n", name);
1281 printf(" type : '%s'\n", typeDescriptor);
1282 printf(" access : 0x%04x (%s)\n",
1283 pSField->accessFlags, accessStr);
1284 } else if (gOptions.outputFormat == OUTPUT_XML) {
1285 char* tmp;
1286
1287 printf("<field name=\"%s\"\n", name);
1288
1289 tmp = descriptorToDot(typeDescriptor);
1290 printf(" type=\"%s\"\n", tmp);
1291 free(tmp);
1292
1293 printf(" transient=%s\n",
1294 quotedBool((pSField->accessFlags & ACC_TRANSIENT) != 0));
1295 printf(" volatile=%s\n",
1296 quotedBool((pSField->accessFlags & ACC_VOLATILE) != 0));
1297 // "value=" not knowable w/o parsing annotations
1298 printf(" static=%s\n",
1299 quotedBool((pSField->accessFlags & ACC_STATIC) != 0));
1300 printf(" final=%s\n",
1301 quotedBool((pSField->accessFlags & ACC_FINAL) != 0));
1302 // "deprecated=" not knowable w/o parsing annotations
1303 printf(" visibility=%s\n",
1304 quotedVisibility(pSField->accessFlags));
1305 printf(">\n</field>\n");
1306 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001307
1308 free(accessStr);
1309}
1310
1311/*
1312 * Dump an instance field.
1313 */
1314void dumpIField(const DexFile* pDexFile, const DexField* pIField, int i)
1315{
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001316 dumpSField(pDexFile, pIField, i);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001317}
1318
1319/*
1320 * Dump the class.
The Android Open Source Project99409882009-03-18 22:20:24 -07001321 *
1322 * Note "idx" is a DexClassDef index, not a DexTypeId index.
Andy McFaddend18aff32009-05-06 10:19:16 -07001323 *
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001324 * If "*pLastPackage" is NULL or does not match the current class' package,
1325 * the value will be replaced with a newly-allocated string.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001326 */
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001327void dumpClass(DexFile* pDexFile, int idx, char** pLastPackage)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001328{
1329 const DexTypeList* pInterfaces;
1330 const DexClassDef* pClassDef;
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001331 DexClassData* pClassData = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001332 const u1* pEncodedData;
1333 const char* fileName;
1334 const char* classDescriptor;
1335 const char* superclassDescriptor;
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001336 char* accessStr = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001337 int i;
1338
1339 pClassDef = dexGetClassDef(pDexFile, idx);
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001340
1341 if (gOptions.exportsOnly && (pClassDef->accessFlags & ACC_PUBLIC) == 0) {
1342 //printf("<!-- omitting non-public class %s -->\n",
1343 // classDescriptor);
1344 goto bail;
1345 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001346
1347 pEncodedData = dexGetClassData(pDexFile, pClassDef);
1348 pClassData = dexReadAndVerifyClassData(&pEncodedData, NULL);
1349
1350 if (pClassData == NULL) {
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001351 printf("Trouble reading class data (#%d)\n", idx);
1352 goto bail;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001353 }
Carl Shapirode750892010-06-08 16:37:12 -07001354
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001355 classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001356
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001357 /*
1358 * For the XML output, show the package name. Ideally we'd gather
1359 * up the classes, sort them, and dump them alphabetically so the
1360 * package name wouldn't jump around, but that's not a great plan
1361 * for something that needs to run on the device.
1362 */
1363 if (!(classDescriptor[0] == 'L' &&
1364 classDescriptor[strlen(classDescriptor)-1] == ';'))
1365 {
1366 /* arrays and primitives should not be defined explicitly */
1367 fprintf(stderr, "Malformed class name '%s'\n", classDescriptor);
1368 /* keep going? */
1369 } else if (gOptions.outputFormat == OUTPUT_XML) {
1370 char* mangle;
1371 char* lastSlash;
1372 char* cp;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001373
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001374 mangle = strdup(classDescriptor + 1);
1375 mangle[strlen(mangle)-1] = '\0';
1376
1377 /* reduce to just the package name */
1378 lastSlash = strrchr(mangle, '/');
1379 if (lastSlash != NULL) {
1380 *lastSlash = '\0';
1381 } else {
1382 *mangle = '\0';
1383 }
1384
1385 for (cp = mangle; *cp != '\0'; cp++) {
1386 if (*cp == '/')
1387 *cp = '.';
1388 }
1389
1390 if (*pLastPackage == NULL || strcmp(mangle, *pLastPackage) != 0) {
1391 /* start of a new package */
1392 if (*pLastPackage != NULL)
1393 printf("</package>\n");
1394 printf("<package name=\"%s\"\n>\n", mangle);
1395 free(*pLastPackage);
1396 *pLastPackage = mangle;
1397 } else {
1398 free(mangle);
1399 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001400 }
1401
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001402 accessStr = createAccessFlagStr(pClassDef->accessFlags, kAccessForClass);
1403
1404 if (pClassDef->superclassIdx == kDexNoIndex) {
1405 superclassDescriptor = NULL;
1406 } else {
1407 superclassDescriptor =
1408 dexStringByTypeIdx(pDexFile, pClassDef->superclassIdx);
1409 }
1410
1411 if (gOptions.outputFormat == OUTPUT_PLAIN) {
1412 printf("Class #%d -\n", idx);
1413 printf(" Class descriptor : '%s'\n", classDescriptor);
1414 printf(" Access flags : 0x%04x (%s)\n",
1415 pClassDef->accessFlags, accessStr);
1416
1417 if (superclassDescriptor != NULL)
1418 printf(" Superclass : '%s'\n", superclassDescriptor);
1419
1420 printf(" Interfaces -\n");
1421 } else {
1422 char* tmp;
1423
1424 tmp = descriptorClassToDot(classDescriptor);
1425 printf("<class name=\"%s\"\n", tmp);
1426 free(tmp);
1427
1428 if (superclassDescriptor != NULL) {
1429 tmp = descriptorToDot(superclassDescriptor);
1430 printf(" extends=\"%s\"\n", tmp);
1431 free(tmp);
1432 }
1433 printf(" abstract=%s\n",
1434 quotedBool((pClassDef->accessFlags & ACC_ABSTRACT) != 0));
1435 printf(" static=%s\n",
1436 quotedBool((pClassDef->accessFlags & ACC_STATIC) != 0));
1437 printf(" final=%s\n",
1438 quotedBool((pClassDef->accessFlags & ACC_FINAL) != 0));
1439 // "deprecated=" not knowable w/o parsing annotations
1440 printf(" visibility=%s\n",
1441 quotedVisibility(pClassDef->accessFlags));
1442 printf(">\n");
1443 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001444 pInterfaces = dexGetInterfacesList(pDexFile, pClassDef);
1445 if (pInterfaces != NULL) {
1446 for (i = 0; i < (int) pInterfaces->size; i++)
1447 dumpInterface(pDexFile, dexGetTypeItem(pInterfaces, i), i);
1448 }
1449
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001450 if (gOptions.outputFormat == OUTPUT_PLAIN)
1451 printf(" Static fields -\n");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001452 for (i = 0; i < (int) pClassData->header.staticFieldsSize; i++) {
1453 dumpSField(pDexFile, &pClassData->staticFields[i], i);
1454 }
1455
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001456 if (gOptions.outputFormat == OUTPUT_PLAIN)
1457 printf(" Instance fields -\n");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001458 for (i = 0; i < (int) pClassData->header.instanceFieldsSize; i++) {
1459 dumpIField(pDexFile, &pClassData->instanceFields[i], i);
1460 }
1461
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001462 if (gOptions.outputFormat == OUTPUT_PLAIN)
1463 printf(" Direct methods -\n");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001464 for (i = 0; i < (int) pClassData->header.directMethodsSize; i++) {
1465 dumpMethod(pDexFile, &pClassData->directMethods[i], i);
1466 }
1467
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001468 if (gOptions.outputFormat == OUTPUT_PLAIN)
1469 printf(" Virtual methods -\n");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001470 for (i = 0; i < (int) pClassData->header.virtualMethodsSize; i++) {
1471 dumpMethod(pDexFile, &pClassData->virtualMethods[i], i);
1472 }
1473
1474 // TODO: Annotations.
1475
1476 if (pClassDef->sourceFileIdx != kDexNoIndex)
1477 fileName = dexStringById(pDexFile, pClassDef->sourceFileIdx);
1478 else
1479 fileName = "unknown";
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001480
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001481 if (gOptions.outputFormat == OUTPUT_PLAIN) {
1482 printf(" source_file_idx : %d (%s)\n",
1483 pClassDef->sourceFileIdx, fileName);
1484 printf("\n");
1485 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001486
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001487 if (gOptions.outputFormat == OUTPUT_XML) {
1488 printf("</class>\n");
1489 }
1490
1491bail:
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001492 free(pClassData);
1493 free(accessStr);
1494}
1495
The Android Open Source Project99409882009-03-18 22:20:24 -07001496
1497/*
1498 * Advance "ptr" to ensure 32-bit alignment.
1499 */
1500static inline const u1* align32(const u1* ptr)
1501{
1502 return (u1*) (((int) ptr + 3) & ~0x03);
1503}
1504
Andy McFadden10351272009-03-24 21:30:32 -07001505
1506/*
1507 * Dump a map in the "differential" format.
1508 *
1509 * TODO: show a hex dump of the compressed data. (We can show the
1510 * uncompressed data if we move the compression code to libdex; otherwise
1511 * it's too complex to merit a fast & fragile implementation here.)
1512 */
1513void dumpDifferentialCompressedMap(const u1** pData)
1514{
1515 const u1* data = *pData;
1516 const u1* dataStart = data -1; // format byte already removed
1517 u1 regWidth;
1518 u2 numEntries;
1519
1520 /* standard header */
1521 regWidth = *data++;
1522 numEntries = *data++;
1523 numEntries |= (*data++) << 8;
1524
1525 /* compressed data begins with the compressed data length */
1526 int compressedLen = readUnsignedLeb128(&data);
1527 int addrWidth = 1;
1528 if ((*data & 0x80) != 0)
1529 addrWidth++;
1530
1531 int origLen = 4 + (addrWidth + regWidth) * numEntries;
1532 int compLen = (data - dataStart) + compressedLen;
1533
1534 printf(" (differential compression %d -> %d [%d -> %d])\n",
1535 origLen, compLen,
1536 (addrWidth + regWidth) * numEntries, compressedLen);
1537
1538 /* skip past end of entry */
1539 data += compressedLen;
1540
1541 *pData = data;
1542}
1543
The Android Open Source Project99409882009-03-18 22:20:24 -07001544/*
1545 * Dump register map contents of the current method.
1546 *
1547 * "*pData" should point to the start of the register map data. Advances
1548 * "*pData" to the start of the next map.
1549 */
1550void dumpMethodMap(DexFile* pDexFile, const DexMethod* pDexMethod, int idx,
1551 const u1** pData)
1552{
1553 const u1* data = *pData;
1554 const DexMethodId* pMethodId;
1555 const char* name;
1556 int offset = data - (u1*) pDexFile->pOptHeader;
1557
1558 pMethodId = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
1559 name = dexStringById(pDexFile, pMethodId->nameIdx);
1560 printf(" #%d: 0x%08x %s\n", idx, offset, name);
1561
1562 u1 format;
1563 int addrWidth;
1564
1565 format = *data++;
1566 if (format == 1) { /* kRegMapFormatNone */
1567 /* no map */
1568 printf(" (no map)\n");
1569 addrWidth = 0;
1570 } else if (format == 2) { /* kRegMapFormatCompact8 */
1571 addrWidth = 1;
1572 } else if (format == 3) { /* kRegMapFormatCompact16 */
1573 addrWidth = 2;
Andy McFadden10351272009-03-24 21:30:32 -07001574 } else if (format == 4) { /* kRegMapFormatDifferential */
1575 dumpDifferentialCompressedMap(&data);
1576 goto bail;
The Android Open Source Project99409882009-03-18 22:20:24 -07001577 } else {
1578 printf(" (unknown format %d!)\n", format);
Andy McFadden10351272009-03-24 21:30:32 -07001579 /* don't know how to skip data; failure will cascade to end of class */
1580 goto bail;
The Android Open Source Project99409882009-03-18 22:20:24 -07001581 }
1582
1583 if (addrWidth > 0) {
1584 u1 regWidth;
1585 u2 numEntries;
1586 int idx, addr, byte;
1587
1588 regWidth = *data++;
1589 numEntries = *data++;
1590 numEntries |= (*data++) << 8;
1591
1592 for (idx = 0; idx < numEntries; idx++) {
1593 addr = *data++;
1594 if (addrWidth > 1)
1595 addr |= (*data++) << 8;
1596
1597 printf(" %4x:", addr);
1598 for (byte = 0; byte < regWidth; byte++) {
1599 printf(" %02x", *data++);
1600 }
1601 printf("\n");
1602 }
1603 }
1604
Andy McFadden10351272009-03-24 21:30:32 -07001605bail:
The Android Open Source Project99409882009-03-18 22:20:24 -07001606 //if (addrWidth >= 0)
1607 // *pData = align32(data);
1608 *pData = data;
1609}
1610
1611/*
1612 * Dump the contents of the register map area.
1613 *
1614 * These are only present in optimized DEX files, and the structure is
1615 * not really exposed to other parts of the VM itself. We're going to
1616 * dig through them here, but this is pretty fragile. DO NOT rely on
1617 * this or derive other code from it.
1618 */
1619void dumpRegisterMaps(DexFile* pDexFile)
1620{
1621 const u1* pClassPool = pDexFile->pRegisterMapPool;
1622 const u4* classOffsets;
1623 const u1* ptr;
1624 u4 numClasses;
1625 int baseFileOffset = (u1*) pClassPool - (u1*) pDexFile->pOptHeader;
1626 int idx;
1627
1628 if (pClassPool == NULL) {
1629 printf("No register maps found\n");
1630 return;
1631 }
1632
1633 ptr = pClassPool;
1634 numClasses = get4LE(ptr);
1635 ptr += sizeof(u4);
1636 classOffsets = (const u4*) ptr;
1637
1638 printf("RMAP begins at offset 0x%07x\n", baseFileOffset);
1639 printf("Maps for %d classes\n", numClasses);
1640 for (idx = 0; idx < (int) numClasses; idx++) {
1641 const DexClassDef* pClassDef;
1642 const char* classDescriptor;
1643
1644 pClassDef = dexGetClassDef(pDexFile, idx);
1645 classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
1646
1647 printf("%4d: +%d (0x%08x) %s\n", idx, classOffsets[idx],
1648 baseFileOffset + classOffsets[idx], classDescriptor);
1649
1650 if (classOffsets[idx] == 0)
1651 continue;
1652
1653 /*
1654 * What follows is a series of RegisterMap entries, one for every
1655 * direct method, then one for every virtual method.
1656 */
1657 DexClassData* pClassData;
1658 const u1* pEncodedData;
1659 const u1* data = (u1*) pClassPool + classOffsets[idx];
1660 u2 methodCount;
1661 int i;
1662
1663 pEncodedData = dexGetClassData(pDexFile, pClassDef);
1664 pClassData = dexReadAndVerifyClassData(&pEncodedData, NULL);
1665 if (pClassData == NULL) {
1666 fprintf(stderr, "Trouble reading class data\n");
1667 continue;
1668 }
1669
1670 methodCount = *data++;
1671 methodCount |= (*data++) << 8;
1672 data += 2; /* two pad bytes follow methodCount */
1673 if (methodCount != pClassData->header.directMethodsSize
1674 + pClassData->header.virtualMethodsSize)
1675 {
1676 printf("NOTE: method count discrepancy (%d != %d + %d)\n",
1677 methodCount, pClassData->header.directMethodsSize,
1678 pClassData->header.virtualMethodsSize);
1679 /* this is bad, but keep going anyway */
1680 }
1681
1682 printf(" direct methods: %d\n",
1683 pClassData->header.directMethodsSize);
1684 for (i = 0; i < (int) pClassData->header.directMethodsSize; i++) {
1685 dumpMethodMap(pDexFile, &pClassData->directMethods[i], i, &data);
1686 }
1687
1688 printf(" virtual methods: %d\n",
1689 pClassData->header.virtualMethodsSize);
1690 for (i = 0; i < (int) pClassData->header.virtualMethodsSize; i++) {
1691 dumpMethodMap(pDexFile, &pClassData->virtualMethods[i], i, &data);
1692 }
1693
1694 free(pClassData);
1695 }
1696}
1697
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001698/*
1699 * Dump the requested sections of the file.
1700 */
1701void processDexFile(const char* fileName, DexFile* pDexFile)
1702{
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001703 char* package = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001704 int i;
1705
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001706 if (gOptions.verbose) {
1707 printf("Opened '%s', DEX version '%.3s'\n", fileName,
1708 pDexFile->pHeader->magic +4);
1709 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001710
The Android Open Source Project99409882009-03-18 22:20:24 -07001711 if (gOptions.dumpRegisterMaps) {
1712 dumpRegisterMaps(pDexFile);
1713 return;
1714 }
1715
Andy McFadden0ea77b92010-04-20 14:18:59 -07001716 if (gOptions.showFileHeaders) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001717 dumpFileHeader(pDexFile);
Dan Bornsteine377ef62010-08-31 16:50:00 -07001718 dumpOptDirectory(pDexFile);
Andy McFadden0ea77b92010-04-20 14:18:59 -07001719 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001720
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001721 if (gOptions.outputFormat == OUTPUT_XML)
1722 printf("<api>\n");
1723
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001724 for (i = 0; i < (int) pDexFile->pHeader->classDefsSize; i++) {
1725 if (gOptions.showSectionHeaders)
1726 dumpClassDef(pDexFile, i);
1727
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001728 dumpClass(pDexFile, i, &package);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001729 }
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001730
1731 /* free the last one allocated */
1732 if (package != NULL) {
1733 printf("</package>\n");
1734 free(package);
1735 }
1736
1737 if (gOptions.outputFormat == OUTPUT_XML)
1738 printf("</api>\n");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001739}
1740
1741
1742/*
1743 * Process one file.
1744 */
1745int process(const char* fileName)
1746{
1747 DexFile* pDexFile = NULL;
1748 MemMapping map;
1749 bool mapped = false;
1750 int result = -1;
1751
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001752 if (gOptions.verbose)
1753 printf("Processing '%s'...\n", fileName);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001754
1755 if (dexOpenAndMap(fileName, gOptions.tempFileName, &map, false) != 0)
1756 goto bail;
1757 mapped = true;
1758
Andy McFadden2124cb82009-03-25 15:37:39 -07001759 int flags = kDexParseVerifyChecksum;
1760 if (gOptions.ignoreBadChecksum)
1761 flags |= kDexParseContinueOnError;
1762
1763 pDexFile = dexFileParse(map.addr, map.length, flags);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001764 if (pDexFile == NULL) {
1765 fprintf(stderr, "ERROR: DEX parse failed\n");
1766 goto bail;
1767 }
1768
Andy McFadden0198b142009-04-02 14:48:56 -07001769 if (gOptions.checksumOnly) {
1770 printf("Checksum verified\n");
1771 } else {
1772 processDexFile(fileName, pDexFile);
1773 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001774
1775 result = 0;
1776
1777bail:
1778 if (mapped)
1779 sysReleaseShmem(&map);
1780 if (pDexFile != NULL)
1781 dexFileFree(pDexFile);
1782 return result;
1783}
1784
1785
1786/*
1787 * Show usage.
1788 */
1789void usage(void)
1790{
1791 fprintf(stderr, "Copyright (C) 2007 The Android Open Source Project\n\n");
Andy McFadden0198b142009-04-02 14:48:56 -07001792 fprintf(stderr,
Andy McFaddend18aff32009-05-06 10:19:16 -07001793 "%s: [-c] [-d] [-f] [-h] [-i] [-l layout] [-m] [-t tempfile] dexfile...\n",
The Android Open Source Project99409882009-03-18 22:20:24 -07001794 gProgName);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001795 fprintf(stderr, "\n");
Andy McFadden0198b142009-04-02 14:48:56 -07001796 fprintf(stderr, " -c : verify checksum and exit\n");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001797 fprintf(stderr, " -d : disassemble code sections\n");
1798 fprintf(stderr, " -f : display summary information from file header\n");
1799 fprintf(stderr, " -h : display file header details\n");
Andy McFadden2124cb82009-03-25 15:37:39 -07001800 fprintf(stderr, " -i : ignore checksum failures\n");
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001801 fprintf(stderr, " -l : output layout, either 'plain' or 'xml'\n");
The Android Open Source Project99409882009-03-18 22:20:24 -07001802 fprintf(stderr, " -m : dump register maps (and nothing else)\n");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001803 fprintf(stderr, " -t : temp file name (defaults to /sdcard/dex-temp-*)\n");
1804}
1805
1806/*
1807 * Parse args.
1808 *
1809 * I'm not using getopt_long() because we may not have it in libc.
1810 */
1811int main(int argc, char* const argv[])
1812{
1813 bool wantUsage = false;
1814 int ic;
1815
1816 memset(&gOptions, 0, sizeof(gOptions));
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001817 gOptions.verbose = true;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001818
1819 while (1) {
Andy McFaddend18aff32009-05-06 10:19:16 -07001820 ic = getopt(argc, argv, "cdfhil:mt:");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001821 if (ic < 0)
1822 break;
1823
1824 switch (ic) {
Andy McFadden0198b142009-04-02 14:48:56 -07001825 case 'c': // verify the checksum then exit
1826 gOptions.checksumOnly = true;
1827 break;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001828 case 'd': // disassemble Dalvik instructions
1829 gOptions.disassemble = true;
1830 break;
1831 case 'f': // dump outer file header
1832 gOptions.showFileHeaders = true;
1833 break;
1834 case 'h': // dump section headers, i.e. all meta-data
1835 gOptions.showSectionHeaders = true;
1836 break;
Andy McFadden2124cb82009-03-25 15:37:39 -07001837 case 'i': // continue even if checksum is bad
1838 gOptions.ignoreBadChecksum = true;
1839 break;
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001840 case 'l': // layout
1841 if (strcmp(optarg, "plain") == 0) {
1842 gOptions.outputFormat = OUTPUT_PLAIN;
1843 } else if (strcmp(optarg, "xml") == 0) {
1844 gOptions.outputFormat = OUTPUT_XML;
1845 gOptions.verbose = false;
1846 gOptions.exportsOnly = true;
1847 } else {
1848 wantUsage = true;
1849 }
1850 break;
The Android Open Source Project99409882009-03-18 22:20:24 -07001851 case 'm': // dump register maps only
1852 gOptions.dumpRegisterMaps = true;
1853 break;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001854 case 't': // temp file, used when opening compressed Jar
Andy McFaddena2ee53b2009-05-05 16:52:10 -07001855 gOptions.tempFileName = optarg;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001856 break;
1857 default:
1858 wantUsage = true;
1859 break;
1860 }
1861 }
1862
1863 if (optind == argc) {
1864 fprintf(stderr, "%s: no file specified\n", gProgName);
1865 wantUsage = true;
1866 }
1867
Andy McFadden0198b142009-04-02 14:48:56 -07001868 if (gOptions.checksumOnly && gOptions.ignoreBadChecksum) {
1869 fprintf(stderr, "Can't specify both -c and -i\n");
1870 wantUsage = true;
1871 }
1872
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001873 /* initialize some VM tables */
1874 gInstrWidth = dexCreateInstrWidthTable();
1875 gInstrFormat = dexCreateInstrFormatTable();
1876
1877 if (wantUsage) {
1878 usage();
1879 return 2;
1880 }
1881
Andy McFadden0198b142009-04-02 14:48:56 -07001882 int result = 0;
1883 while (optind < argc) {
1884 result |= process(argv[optind++]);
1885 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001886
1887 free(gInstrWidth);
1888 free(gInstrFormat);
1889
Andy McFadden0198b142009-04-02 14:48:56 -07001890 return (result != 0);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001891}