blob: 84c465fb8a01db81ac98dcc296eedadc7ab5d948 [file] [log] [blame]
Aart Bik69ae54a2015-07-01 14:52:26 -07001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 * Implementation file of the dexdump utility.
17 *
18 * This is a re-implementation of the original dexdump utility that was
19 * based on Dalvik functions in libdex into a new dexdump that is now
20 * based on Art functions in libart instead. The output is identical to
21 * the original for correct DEX files. Error messages may differ, however.
22 * Also, ODEX files are no longer supported.
23 *
24 * The dexdump tool is intended to mimic objdump. When possible, use
25 * similar command-line arguments.
26 *
27 * Differences between XML output and the "current.xml" file:
28 * - classes in same package are not all grouped together; nothing is sorted
29 * - no "deprecated" on fields and methods
30 * - no "value" on fields
31 * - no parameter names
32 * - no generic signatures on parameters, e.g. type="java.lang.Class<?>"
33 * - class shows declared fields and methods; does not show inherited fields
34 */
35
36#include "dexdump.h"
37
38#include <inttypes.h>
39#include <stdio.h>
40
41#include <memory>
42#include <vector>
43
44#include "dex_file-inl.h"
45#include "dex_instruction-inl.h"
46
47namespace art {
48
49/*
50 * Options parsed in main driver.
51 */
52struct Options gOptions;
53
54/*
Aart Bik4e149602015-07-09 11:45:28 -070055 * Output file. Defaults to stdout.
Aart Bik69ae54a2015-07-01 14:52:26 -070056 */
57FILE* gOutFile = stdout;
58
59/*
60 * Data types that match the definitions in the VM specification.
61 */
62typedef uint8_t u1;
63typedef uint16_t u2;
64typedef uint32_t u4;
65typedef uint64_t u8;
Aart Bik69ae54a2015-07-01 14:52:26 -070066typedef int32_t s4;
67typedef int64_t s8;
68
69/*
70 * Basic information about a field or a method.
71 */
72struct FieldMethodInfo {
73 const char* classDescriptor;
74 const char* name;
75 const char* signature;
76};
77
78/*
79 * Flags for use with createAccessFlagStr().
80 */
81enum AccessFor {
82 kAccessForClass = 0, kAccessForMethod = 1, kAccessForField = 2, kAccessForMAX
83};
84const int kNumFlags = 18;
85
86/*
87 * Gets 2 little-endian bytes.
88 */
89static inline u2 get2LE(unsigned char const* pSrc) {
90 return pSrc[0] | (pSrc[1] << 8);
91}
92
93/*
94 * Converts a single-character primitive type into human-readable form.
95 */
96static const char* primitiveTypeLabel(char typeChar) {
97 switch (typeChar) {
98 case 'B': return "byte";
99 case 'C': return "char";
100 case 'D': return "double";
101 case 'F': return "float";
102 case 'I': return "int";
103 case 'J': return "long";
104 case 'S': return "short";
105 case 'V': return "void";
106 case 'Z': return "boolean";
107 default: return "UNKNOWN";
108 } // switch
109}
110
111/*
112 * Converts a type descriptor to human-readable "dotted" form. For
113 * example, "Ljava/lang/String;" becomes "java.lang.String", and
114 * "[I" becomes "int[]". Also converts '$' to '.', which means this
115 * form can't be converted back to a descriptor.
116 */
117static char* descriptorToDot(const char* str) {
118 int targetLen = strlen(str);
119 int offset = 0;
120
121 // Strip leading [s; will be added to end.
122 while (targetLen > 1 && str[offset] == '[') {
123 offset++;
124 targetLen--;
125 } // while
126
127 const int arrayDepth = offset;
128
129 if (targetLen == 1) {
130 // Primitive type.
131 str = primitiveTypeLabel(str[offset]);
132 offset = 0;
133 targetLen = strlen(str);
134 } else {
135 // Account for leading 'L' and trailing ';'.
136 if (targetLen >= 2 && str[offset] == 'L' &&
137 str[offset + targetLen - 1] == ';') {
138 targetLen -= 2;
139 offset++;
140 }
141 }
142
143 // Copy class name over.
144 char* newStr = reinterpret_cast<char*>(
145 malloc(targetLen + arrayDepth * 2 + 1));
146 int i = 0;
147 for (; i < targetLen; i++) {
148 const char ch = str[offset + i];
149 newStr[i] = (ch == '/' || ch == '$') ? '.' : ch;
150 } // for
151
152 // Add the appropriate number of brackets for arrays.
153 for (int j = 0; j < arrayDepth; j++) {
154 newStr[i++] = '[';
155 newStr[i++] = ']';
156 } // for
157
158 newStr[i] = '\0';
159 return newStr;
160}
161
162/*
163 * Converts the class name portion of a type descriptor to human-readable
164 * "dotted" form.
165 *
166 * Returns a newly-allocated string.
167 */
168static char* descriptorClassToDot(const char* str) {
169 // Reduce to just the class name, trimming trailing ';'.
170 const char* lastSlash = strrchr(str, '/');
171 if (lastSlash == nullptr) {
172 lastSlash = str + 1; // start past 'L'
173 } else {
174 lastSlash++; // start past '/'
175 }
176
177 char* newStr = strdup(lastSlash);
178 newStr[strlen(lastSlash) - 1] = '\0';
179 for (char* cp = newStr; *cp != '\0'; cp++) {
180 if (*cp == '$') {
181 *cp = '.';
182 }
183 } // for
184 return newStr;
185}
186
187/*
188 * Returns a quoted string representing the boolean value.
189 */
190static const char* quotedBool(bool val) {
191 return val ? "\"true\"" : "\"false\"";
192}
193
194/*
195 * Returns a quoted string representing the access flags.
196 */
197static const char* quotedVisibility(u4 accessFlags) {
198 if (accessFlags & kAccPublic) {
199 return "\"public\"";
200 } else if (accessFlags & kAccProtected) {
201 return "\"protected\"";
202 } else if (accessFlags & kAccPrivate) {
203 return "\"private\"";
204 } else {
205 return "\"package\"";
206 }
207}
208
209/*
210 * Counts the number of '1' bits in a word.
211 */
212static int countOnes(u4 val) {
213 val = val - ((val >> 1) & 0x55555555);
214 val = (val & 0x33333333) + ((val >> 2) & 0x33333333);
215 return (((val + (val >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
216}
217
218/*
219 * Creates a new string with human-readable access flags.
220 *
221 * In the base language the access_flags fields are type u2; in Dalvik
222 * they're u4.
223 */
224static char* createAccessFlagStr(u4 flags, AccessFor forWhat) {
225 static const char* kAccessStrings[kAccessForMAX][kNumFlags] = {
226 {
227 "PUBLIC", /* 0x00001 */
228 "PRIVATE", /* 0x00002 */
229 "PROTECTED", /* 0x00004 */
230 "STATIC", /* 0x00008 */
231 "FINAL", /* 0x00010 */
232 "?", /* 0x00020 */
233 "?", /* 0x00040 */
234 "?", /* 0x00080 */
235 "?", /* 0x00100 */
236 "INTERFACE", /* 0x00200 */
237 "ABSTRACT", /* 0x00400 */
238 "?", /* 0x00800 */
239 "SYNTHETIC", /* 0x01000 */
240 "ANNOTATION", /* 0x02000 */
241 "ENUM", /* 0x04000 */
242 "?", /* 0x08000 */
243 "VERIFIED", /* 0x10000 */
244 "OPTIMIZED", /* 0x20000 */
245 }, {
246 "PUBLIC", /* 0x00001 */
247 "PRIVATE", /* 0x00002 */
248 "PROTECTED", /* 0x00004 */
249 "STATIC", /* 0x00008 */
250 "FINAL", /* 0x00010 */
251 "SYNCHRONIZED", /* 0x00020 */
252 "BRIDGE", /* 0x00040 */
253 "VARARGS", /* 0x00080 */
254 "NATIVE", /* 0x00100 */
255 "?", /* 0x00200 */
256 "ABSTRACT", /* 0x00400 */
257 "STRICT", /* 0x00800 */
258 "SYNTHETIC", /* 0x01000 */
259 "?", /* 0x02000 */
260 "?", /* 0x04000 */
261 "MIRANDA", /* 0x08000 */
262 "CONSTRUCTOR", /* 0x10000 */
263 "DECLARED_SYNCHRONIZED", /* 0x20000 */
264 }, {
265 "PUBLIC", /* 0x00001 */
266 "PRIVATE", /* 0x00002 */
267 "PROTECTED", /* 0x00004 */
268 "STATIC", /* 0x00008 */
269 "FINAL", /* 0x00010 */
270 "?", /* 0x00020 */
271 "VOLATILE", /* 0x00040 */
272 "TRANSIENT", /* 0x00080 */
273 "?", /* 0x00100 */
274 "?", /* 0x00200 */
275 "?", /* 0x00400 */
276 "?", /* 0x00800 */
277 "SYNTHETIC", /* 0x01000 */
278 "?", /* 0x02000 */
279 "ENUM", /* 0x04000 */
280 "?", /* 0x08000 */
281 "?", /* 0x10000 */
282 "?", /* 0x20000 */
283 },
284 };
285
286 // Allocate enough storage to hold the expected number of strings,
287 // plus a space between each. We over-allocate, using the longest
288 // string above as the base metric.
289 const int kLongest = 21; // The strlen of longest string above.
290 const int count = countOnes(flags);
291 char* str;
292 char* cp;
293 cp = str = reinterpret_cast<char*>(malloc(count * (kLongest + 1) + 1));
294
295 for (int i = 0; i < kNumFlags; i++) {
296 if (flags & 0x01) {
297 const char* accessStr = kAccessStrings[forWhat][i];
298 const int len = strlen(accessStr);
299 if (cp != str) {
300 *cp++ = ' ';
301 }
302 memcpy(cp, accessStr, len);
303 cp += len;
304 }
305 flags >>= 1;
306 } // for
307
308 *cp = '\0';
309 return str;
310}
311
312/*
313 * Copies character data from "data" to "out", converting non-ASCII values
314 * to fprintf format chars or an ASCII filler ('.' or '?').
315 *
316 * The output buffer must be able to hold (2*len)+1 bytes. The result is
317 * NULL-terminated.
318 */
319static void asciify(char* out, const unsigned char* data, size_t len) {
320 while (len--) {
321 if (*data < 0x20) {
322 // Could do more here, but we don't need them yet.
323 switch (*data) {
324 case '\0':
325 *out++ = '\\';
326 *out++ = '0';
327 break;
328 case '\n':
329 *out++ = '\\';
330 *out++ = 'n';
331 break;
332 default:
333 *out++ = '.';
334 break;
335 } // switch
336 } else if (*data >= 0x80) {
337 *out++ = '?';
338 } else {
339 *out++ = *data;
340 }
341 data++;
342 } // while
343 *out = '\0';
344}
345
346/*
347 * Dumps the file header.
348 *
349 * Note that some of the : are misaligned on purpose to preserve
350 * the exact output of the original Dalvik dexdump.
351 */
352static void dumpFileHeader(const DexFile* pDexFile) {
353 const DexFile::Header& pHeader = pDexFile->GetHeader();
354 char sanitized[sizeof(pHeader.magic_) * 2 + 1];
355 fprintf(gOutFile, "DEX file header:\n");
356 asciify(sanitized, pHeader.magic_, sizeof(pHeader.magic_));
357 fprintf(gOutFile, "magic : '%s'\n", sanitized);
358 fprintf(gOutFile, "checksum : %08x\n", pHeader.checksum_);
359 fprintf(gOutFile, "signature : %02x%02x...%02x%02x\n",
360 pHeader.signature_[0], pHeader.signature_[1],
361 pHeader.signature_[DexFile::kSha1DigestSize - 2],
362 pHeader.signature_[DexFile::kSha1DigestSize - 1]);
363 fprintf(gOutFile, "file_size : %d\n", pHeader.file_size_);
364 fprintf(gOutFile, "header_size : %d\n", pHeader.header_size_);
365 fprintf(gOutFile, "link_size : %d\n", pHeader.link_size_);
366 fprintf(gOutFile, "link_off : %d (0x%06x)\n",
367 pHeader.link_off_, pHeader.link_off_);
368 fprintf(gOutFile, "string_ids_size : %d\n", pHeader.string_ids_size_);
369 fprintf(gOutFile, "string_ids_off : %d (0x%06x)\n",
370 pHeader.string_ids_off_, pHeader.string_ids_off_);
371 fprintf(gOutFile, "type_ids_size : %d\n", pHeader.type_ids_size_);
372 fprintf(gOutFile, "type_ids_off : %d (0x%06x)\n",
373 pHeader.type_ids_off_, pHeader.type_ids_off_);
374 fprintf(gOutFile, "proto_ids_size : %d\n", pHeader.proto_ids_size_);
375 fprintf(gOutFile, "proto_ids_off : %d (0x%06x)\n",
376 pHeader.proto_ids_off_, pHeader.proto_ids_off_);
377 fprintf(gOutFile, "field_ids_size : %d\n", pHeader.field_ids_size_);
378 fprintf(gOutFile, "field_ids_off : %d (0x%06x)\n",
379 pHeader.field_ids_off_, pHeader.field_ids_off_);
380 fprintf(gOutFile, "method_ids_size : %d\n", pHeader.method_ids_size_);
381 fprintf(gOutFile, "method_ids_off : %d (0x%06x)\n",
382 pHeader.method_ids_off_, pHeader.method_ids_off_);
383 fprintf(gOutFile, "class_defs_size : %d\n", pHeader.class_defs_size_);
384 fprintf(gOutFile, "class_defs_off : %d (0x%06x)\n",
385 pHeader.class_defs_off_, pHeader.class_defs_off_);
386 fprintf(gOutFile, "data_size : %d\n", pHeader.data_size_);
387 fprintf(gOutFile, "data_off : %d (0x%06x)\n\n",
388 pHeader.data_off_, pHeader.data_off_);
389}
390
391/*
392 * Dumps a class_def_item.
393 */
394static void dumpClassDef(const DexFile* pDexFile, int idx) {
395 // General class information.
396 const DexFile::ClassDef& pClassDef = pDexFile->GetClassDef(idx);
397 fprintf(gOutFile, "Class #%d header:\n", idx);
398 fprintf(gOutFile, "class_idx : %d\n", pClassDef.class_idx_);
399 fprintf(gOutFile, "access_flags : %d (0x%04x)\n",
400 pClassDef.access_flags_, pClassDef.access_flags_);
401 fprintf(gOutFile, "superclass_idx : %d\n", pClassDef.superclass_idx_);
402 fprintf(gOutFile, "interfaces_off : %d (0x%06x)\n",
403 pClassDef.interfaces_off_, pClassDef.interfaces_off_);
404 fprintf(gOutFile, "source_file_idx : %d\n", pClassDef.source_file_idx_);
405 fprintf(gOutFile, "annotations_off : %d (0x%06x)\n",
406 pClassDef.annotations_off_, pClassDef.annotations_off_);
407 fprintf(gOutFile, "class_data_off : %d (0x%06x)\n",
408 pClassDef.class_data_off_, pClassDef.class_data_off_);
409
410 // Fields and methods.
411 const u1* pEncodedData = pDexFile->GetClassData(pClassDef);
412 if (pEncodedData != nullptr) {
413 ClassDataItemIterator pClassData(*pDexFile, pEncodedData);
414 fprintf(gOutFile, "static_fields_size : %d\n", pClassData.NumStaticFields());
415 fprintf(gOutFile, "instance_fields_size: %d\n", pClassData.NumInstanceFields());
416 fprintf(gOutFile, "direct_methods_size : %d\n", pClassData.NumDirectMethods());
417 fprintf(gOutFile, "virtual_methods_size: %d\n", pClassData.NumVirtualMethods());
418 } else {
419 fprintf(gOutFile, "static_fields_size : 0\n");
420 fprintf(gOutFile, "instance_fields_size: 0\n");
421 fprintf(gOutFile, "direct_methods_size : 0\n");
422 fprintf(gOutFile, "virtual_methods_size: 0\n");
423 }
424 fprintf(gOutFile, "\n");
425}
426
427/*
428 * Dumps an interface that a class declares to implement.
429 */
430static void dumpInterface(const DexFile* pDexFile, const DexFile::TypeItem& pTypeItem, int i) {
431 const char* interfaceName = pDexFile->StringByTypeIdx(pTypeItem.type_idx_);
432 if (gOptions.outputFormat == OUTPUT_PLAIN) {
433 fprintf(gOutFile, " #%d : '%s'\n", i, interfaceName);
434 } else {
435 char* dotted = descriptorToDot(interfaceName);
436 fprintf(gOutFile, "<implements name=\"%s\">\n</implements>\n", dotted);
437 free(dotted);
438 }
439}
440
441/*
442 * Dumps the catches table associated with the code.
443 */
444static void dumpCatches(const DexFile* pDexFile, const DexFile::CodeItem* pCode) {
445 const u4 triesSize = pCode->tries_size_;
446
447 // No catch table.
448 if (triesSize == 0) {
449 fprintf(gOutFile, " catches : (none)\n");
450 return;
451 }
452
453 // Dump all table entries.
454 fprintf(gOutFile, " catches : %d\n", triesSize);
455 for (u4 i = 0; i < triesSize; i++) {
456 const DexFile::TryItem* pTry = pDexFile->GetTryItems(*pCode, i);
457 const u4 start = pTry->start_addr_;
458 const u4 end = start + pTry->insn_count_;
459 fprintf(gOutFile, " 0x%04x - 0x%04x\n", start, end);
460 for (CatchHandlerIterator it(*pCode, *pTry); it.HasNext(); it.Next()) {
461 const u2 tidx = it.GetHandlerTypeIndex();
462 const char* descriptor =
463 (tidx == DexFile::kDexNoIndex16) ? "<any>" : pDexFile->StringByTypeIdx(tidx);
464 fprintf(gOutFile, " %s -> 0x%04x\n", descriptor, it.GetHandlerAddress());
465 } // for
466 } // for
467}
468
469/*
470 * Callback for dumping each positions table entry.
471 */
472static bool dumpPositionsCb(void* /*context*/, u4 address, u4 lineNum) {
473 fprintf(gOutFile, " 0x%04x line=%d\n", address, lineNum);
474 return false;
475}
476
477/*
478 * Callback for dumping locals table entry.
479 */
480static void dumpLocalsCb(void* /*context*/, u2 slot, u4 startAddress, u4 endAddress,
481 const char* name, const char* descriptor, const char* signature) {
482 fprintf(gOutFile, " 0x%04x - 0x%04x reg=%d %s %s %s\n",
483 startAddress, endAddress, slot, name, descriptor, signature);
484}
485
486/*
487 * Helper for dumpInstruction(), which builds the string
488 * representation for the index in the given instruction. This will
489 * first try to use the given buffer, but if the result won't fit,
490 * then this will allocate a new buffer to hold the result. A pointer
491 * to the buffer which holds the full result is always returned, and
492 * this can be compared with the one passed in, to see if the result
493 * needs to be free()d.
494 */
495static char* indexString(const DexFile* pDexFile,
496 const Instruction* pDecInsn, char* buf, size_t bufSize) {
497 // Determine index and width of the string.
498 u4 index = 0;
499 u4 width = 4;
500 switch (Instruction::FormatOf(pDecInsn->Opcode())) {
501 // SOME NOT SUPPORTED:
502 // case Instruction::k20bc:
503 case Instruction::k21c:
504 case Instruction::k35c:
505 // case Instruction::k35ms:
506 case Instruction::k3rc:
507 // case Instruction::k3rms:
508 // case Instruction::k35mi:
509 // case Instruction::k3rmi:
510 index = pDecInsn->VRegB();
511 width = 4;
512 break;
513 case Instruction::k31c:
514 index = pDecInsn->VRegB();
515 width = 8;
516 break;
517 case Instruction::k22c:
518 // case Instruction::k22cs:
519 index = pDecInsn->VRegC();
520 width = 4;
521 break;
522 default:
523 break;
524 } // switch
525
526 // Determine index type.
527 size_t outSize = 0;
528 switch (Instruction::IndexTypeOf(pDecInsn->Opcode())) {
529 case Instruction::kIndexUnknown:
530 // This function should never get called for this type, but do
531 // something sensible here, just to help with debugging.
532 outSize = snprintf(buf, bufSize, "<unknown-index>");
533 break;
534 case Instruction::kIndexNone:
535 // This function should never get called for this type, but do
536 // something sensible here, just to help with debugging.
537 outSize = snprintf(buf, bufSize, "<no-index>");
538 break;
539 case Instruction::kIndexTypeRef:
540 if (index < pDexFile->GetHeader().type_ids_size_) {
541 const char* tp = pDexFile->StringByTypeIdx(index);
542 outSize = snprintf(buf, bufSize, "%s // type@%0*x", tp, width, index);
543 } else {
544 outSize = snprintf(buf, bufSize, "<type?> // type@%0*x", width, index);
545 }
546 break;
547 case Instruction::kIndexStringRef:
548 if (index < pDexFile->GetHeader().string_ids_size_) {
549 const char* st = pDexFile->StringDataByIdx(index);
550 outSize = snprintf(buf, bufSize, "\"%s\" // string@%0*x", st, width, index);
551 } else {
552 outSize = snprintf(buf, bufSize, "<string?> // string@%0*x", width, index);
553 }
554 break;
555 case Instruction::kIndexMethodRef:
556 if (index < pDexFile->GetHeader().method_ids_size_) {
557 const DexFile::MethodId& pMethodId = pDexFile->GetMethodId(index);
558 const char* name = pDexFile->StringDataByIdx(pMethodId.name_idx_);
559 const Signature signature = pDexFile->GetMethodSignature(pMethodId);
560 const char* backDescriptor = pDexFile->StringByTypeIdx(pMethodId.class_idx_);
561 outSize = snprintf(buf, bufSize, "%s.%s:%s // method@%0*x",
562 backDescriptor, name, signature.ToString().c_str(), width, index);
563 } else {
564 outSize = snprintf(buf, bufSize, "<method?> // method@%0*x", width, index);
565 }
566 break;
567 case Instruction::kIndexFieldRef:
568 if (index < pDexFile->GetHeader().field_ids_size_) {
569 const DexFile::FieldId& pFieldId = pDexFile->GetFieldId(index);
570 const char* name = pDexFile->StringDataByIdx(pFieldId.name_idx_);
571 const char* typeDescriptor = pDexFile->StringByTypeIdx(pFieldId.type_idx_);
572 const char* backDescriptor = pDexFile->StringByTypeIdx(pFieldId.class_idx_);
573 outSize = snprintf(buf, bufSize, "%s.%s:%s // field@%0*x",
574 backDescriptor, name, typeDescriptor, width, index);
575 } else {
576 outSize = snprintf(buf, bufSize, "<field?> // field@%0*x", width, index);
577 }
578 break;
579 case Instruction::kIndexVtableOffset:
580 outSize = snprintf(buf, bufSize, "[%0*x] // vtable #%0*x",
581 width, index, width, index);
582 break;
583 case Instruction::kIndexFieldOffset:
584 outSize = snprintf(buf, bufSize, "[obj+%0*x]", width, index);
585 break;
586 // SOME NOT SUPPORTED:
587 // case Instruction::kIndexVaries:
588 // case Instruction::kIndexInlineMethod:
589 default:
590 outSize = snprintf(buf, bufSize, "<?>");
591 break;
592 } // switch
593
594 // Determine success of string construction.
595 if (outSize >= bufSize) {
596 // The buffer wasn't big enough; allocate and retry. Note:
597 // snprintf() doesn't count the '\0' as part of its returned
598 // size, so we add explicit space for it here.
599 outSize++;
600 buf = reinterpret_cast<char*>(malloc(outSize));
601 if (buf == nullptr) {
602 return nullptr;
603 }
604 return indexString(pDexFile, pDecInsn, buf, outSize);
605 }
606 return buf;
607}
608
609/*
610 * Dumps a single instruction.
611 */
612static void dumpInstruction(const DexFile* pDexFile,
613 const DexFile::CodeItem* pCode,
614 u4 codeOffset, u4 insnIdx, u4 insnWidth,
615 const Instruction* pDecInsn) {
616 // Address of instruction (expressed as byte offset).
617 fprintf(gOutFile, "%06x:", codeOffset + 0x10 + insnIdx * 2);
618
619 // Dump (part of) raw bytes.
620 const u2* insns = pCode->insns_;
621 for (u4 i = 0; i < 8; i++) {
622 if (i < insnWidth) {
623 if (i == 7) {
624 fprintf(gOutFile, " ... ");
625 } else {
626 // Print 16-bit value in little-endian order.
627 const u1* bytePtr = (const u1*) &insns[insnIdx + i];
628 fprintf(gOutFile, " %02x%02x", bytePtr[0], bytePtr[1]);
629 }
630 } else {
631 fputs(" ", gOutFile);
632 }
633 } // for
634
635 // Dump pseudo-instruction or opcode.
636 if (pDecInsn->Opcode() == Instruction::NOP) {
637 const u2 instr = get2LE((const u1*) &insns[insnIdx]);
638 if (instr == Instruction::kPackedSwitchSignature) {
639 fprintf(gOutFile, "|%04x: packed-switch-data (%d units)", insnIdx, insnWidth);
640 } else if (instr == Instruction::kSparseSwitchSignature) {
641 fprintf(gOutFile, "|%04x: sparse-switch-data (%d units)", insnIdx, insnWidth);
642 } else if (instr == Instruction::kArrayDataSignature) {
643 fprintf(gOutFile, "|%04x: array-data (%d units)", insnIdx, insnWidth);
644 } else {
645 fprintf(gOutFile, "|%04x: nop // spacer", insnIdx);
646 }
647 } else {
648 fprintf(gOutFile, "|%04x: %s", insnIdx, pDecInsn->Name());
649 }
650
651 // Set up additional argument.
652 char indexBufChars[200];
653 char *indexBuf = indexBufChars;
654 if (Instruction::IndexTypeOf(pDecInsn->Opcode()) != Instruction::kIndexNone) {
655 indexBuf = indexString(pDexFile, pDecInsn,
656 indexBufChars, sizeof(indexBufChars));
657 }
658
659 // Dump the instruction.
660 //
661 // NOTE: pDecInsn->DumpString(pDexFile) differs too much from original.
662 //
663 switch (Instruction::FormatOf(pDecInsn->Opcode())) {
664 case Instruction::k10x: // op
665 break;
666 case Instruction::k12x: // op vA, vB
667 fprintf(gOutFile, " v%d, v%d", pDecInsn->VRegA(), pDecInsn->VRegB());
668 break;
669 case Instruction::k11n: // op vA, #+B
670 fprintf(gOutFile, " v%d, #int %d // #%x",
671 pDecInsn->VRegA(), (s4) pDecInsn->VRegB(), (u1)pDecInsn->VRegB());
672 break;
673 case Instruction::k11x: // op vAA
674 fprintf(gOutFile, " v%d", pDecInsn->VRegA());
675 break;
676 case Instruction::k10t: // op +AA
677 case Instruction::k20t: // op +AAAA
678 {
679 const s4 targ = (s4) pDecInsn->VRegA();
680 fprintf(gOutFile, " %04x // %c%04x",
681 insnIdx + targ,
682 (targ < 0) ? '-' : '+',
683 (targ < 0) ? -targ : targ);
684 }
685 break;
686 case Instruction::k22x: // op vAA, vBBBB
687 fprintf(gOutFile, " v%d, v%d", pDecInsn->VRegA(), pDecInsn->VRegB());
688 break;
689 case Instruction::k21t: // op vAA, +BBBB
690 {
691 const s4 targ = (s4) pDecInsn->VRegB();
692 fprintf(gOutFile, " v%d, %04x // %c%04x", pDecInsn->VRegA(),
693 insnIdx + targ,
694 (targ < 0) ? '-' : '+',
695 (targ < 0) ? -targ : targ);
696 }
697 break;
698 case Instruction::k21s: // op vAA, #+BBBB
699 fprintf(gOutFile, " v%d, #int %d // #%x",
700 pDecInsn->VRegA(), (s4) pDecInsn->VRegB(), (u2)pDecInsn->VRegB());
701 break;
702 case Instruction::k21h: // op vAA, #+BBBB0000[00000000]
703 // The printed format varies a bit based on the actual opcode.
704 if (pDecInsn->Opcode() == Instruction::CONST_HIGH16) {
705 const s4 value = pDecInsn->VRegB() << 16;
706 fprintf(gOutFile, " v%d, #int %d // #%x",
707 pDecInsn->VRegA(), value, (u2) pDecInsn->VRegB());
708 } else {
709 const s8 value = ((s8) pDecInsn->VRegB()) << 48;
710 fprintf(gOutFile, " v%d, #long %" PRId64 " // #%x",
711 pDecInsn->VRegA(), value, (u2) pDecInsn->VRegB());
712 }
713 break;
714 case Instruction::k21c: // op vAA, thing@BBBB
715 case Instruction::k31c: // op vAA, thing@BBBBBBBB
716 fprintf(gOutFile, " v%d, %s", pDecInsn->VRegA(), indexBuf);
717 break;
718 case Instruction::k23x: // op vAA, vBB, vCC
719 fprintf(gOutFile, " v%d, v%d, v%d",
720 pDecInsn->VRegA(), pDecInsn->VRegB(), pDecInsn->VRegC());
721 break;
722 case Instruction::k22b: // op vAA, vBB, #+CC
723 fprintf(gOutFile, " v%d, v%d, #int %d // #%02x",
724 pDecInsn->VRegA(), pDecInsn->VRegB(),
725 (s4) pDecInsn->VRegC(), (u1) pDecInsn->VRegC());
726 break;
727 case Instruction::k22t: // op vA, vB, +CCCC
728 {
729 const s4 targ = (s4) pDecInsn->VRegC();
730 fprintf(gOutFile, " v%d, v%d, %04x // %c%04x",
731 pDecInsn->VRegA(), pDecInsn->VRegB(),
732 insnIdx + targ,
733 (targ < 0) ? '-' : '+',
734 (targ < 0) ? -targ : targ);
735 }
736 break;
737 case Instruction::k22s: // op vA, vB, #+CCCC
738 fprintf(gOutFile, " v%d, v%d, #int %d // #%04x",
739 pDecInsn->VRegA(), pDecInsn->VRegB(),
740 (s4) pDecInsn->VRegC(), (u2) pDecInsn->VRegC());
741 break;
742 case Instruction::k22c: // op vA, vB, thing@CCCC
743 // NOT SUPPORTED:
744 // case Instruction::k22cs: // [opt] op vA, vB, field offset CCCC
745 fprintf(gOutFile, " v%d, v%d, %s",
746 pDecInsn->VRegA(), pDecInsn->VRegB(), indexBuf);
747 break;
748 case Instruction::k30t:
749 fprintf(gOutFile, " #%08x", pDecInsn->VRegA());
750 break;
751 case Instruction::k31i: // op vAA, #+BBBBBBBB
752 {
753 // This is often, but not always, a float.
754 union {
755 float f;
756 u4 i;
757 } conv;
758 conv.i = pDecInsn->VRegB();
759 fprintf(gOutFile, " v%d, #float %f // #%08x",
760 pDecInsn->VRegA(), conv.f, pDecInsn->VRegB());
761 }
762 break;
763 case Instruction::k31t: // op vAA, offset +BBBBBBBB
764 fprintf(gOutFile, " v%d, %08x // +%08x",
765 pDecInsn->VRegA(), insnIdx + pDecInsn->VRegB(), pDecInsn->VRegB());
766 break;
767 case Instruction::k32x: // op vAAAA, vBBBB
768 fprintf(gOutFile, " v%d, v%d", pDecInsn->VRegA(), pDecInsn->VRegB());
769 break;
770 case Instruction::k35c: // op {vC, vD, vE, vF, vG}, thing@BBBB
771 // NOT SUPPORTED:
772 // case Instruction::k35ms: // [opt] invoke-virtual+super
773 // case Instruction::k35mi: // [opt] inline invoke
774 {
775 u4 arg[5];
776 pDecInsn->GetVarArgs(arg);
777 fputs(" {", gOutFile);
778 for (int i = 0, n = pDecInsn->VRegA(); i < n; i++) {
779 if (i == 0) {
780 fprintf(gOutFile, "v%d", arg[i]);
781 } else {
782 fprintf(gOutFile, ", v%d", arg[i]);
783 }
784 } // for
785 fprintf(gOutFile, "}, %s", indexBuf);
786 }
787 break;
788 case Instruction::k3rc: // op {vCCCC .. v(CCCC+AA-1)}, thing@BBBB
789 // NOT SUPPORTED:
790 // case Instruction::k3rms: // [opt] invoke-virtual+super/range
791 // case Instruction::k3rmi: // [opt] execute-inline/range
792 {
793 // This doesn't match the "dx" output when some of the args are
794 // 64-bit values -- dx only shows the first register.
795 fputs(" {", gOutFile);
796 for (int i = 0, n = pDecInsn->VRegA(); i < n; i++) {
797 if (i == 0) {
798 fprintf(gOutFile, "v%d", pDecInsn->VRegC() + i);
799 } else {
800 fprintf(gOutFile, ", v%d", pDecInsn->VRegC() + i);
801 }
802 } // for
803 fprintf(gOutFile, "}, %s", indexBuf);
804 }
805 break;
806 case Instruction::k51l: // op vAA, #+BBBBBBBBBBBBBBBB
807 {
808 // This is often, but not always, a double.
809 union {
810 double d;
811 u8 j;
812 } conv;
813 conv.j = pDecInsn->WideVRegB();
814 fprintf(gOutFile, " v%d, #double %f // #%016" PRIx64,
815 pDecInsn->VRegA(), conv.d, pDecInsn->WideVRegB());
816 }
817 break;
818 // NOT SUPPORTED:
819 // case Instruction::k00x: // unknown op or breakpoint
820 // break;
821 default:
822 fprintf(gOutFile, " ???");
823 break;
824 } // switch
825
826 fputc('\n', gOutFile);
827
828 if (indexBuf != indexBufChars) {
829 free(indexBuf);
830 }
831}
832
833/*
834 * Dumps a bytecode disassembly.
835 */
836static void dumpBytecodes(const DexFile* pDexFile, u4 idx,
837 const DexFile::CodeItem* pCode, u4 codeOffset) {
838 const DexFile::MethodId& pMethodId = pDexFile->GetMethodId(idx);
839 const char* name = pDexFile->StringDataByIdx(pMethodId.name_idx_);
840 const Signature signature = pDexFile->GetMethodSignature(pMethodId);
841 const char* backDescriptor = pDexFile->StringByTypeIdx(pMethodId.class_idx_);
842
843 // Generate header.
844 char* tmp = descriptorToDot(backDescriptor);
845 fprintf(gOutFile, "%06x: "
846 "|[%06x] %s.%s:%s\n",
847 codeOffset, codeOffset, tmp, name, signature.ToString().c_str());
848 free(tmp);
849
850 // Iterate over all instructions.
851 const u2* insns = pCode->insns_;
852 for (u4 insnIdx = 0; insnIdx < pCode->insns_size_in_code_units_;) {
853 const Instruction* instruction = Instruction::At(&insns[insnIdx]);
854 const u4 insnWidth = instruction->SizeInCodeUnits();
855 if (insnWidth == 0) {
856 fprintf(stderr, "GLITCH: zero-width instruction at idx=0x%04x\n", insnIdx);
857 break;
858 }
859 dumpInstruction(pDexFile, pCode, codeOffset, insnIdx, insnWidth, instruction);
860 insnIdx += insnWidth;
861 } // for
862}
863
864/*
865 * Dumps code of a method.
866 */
867static void dumpCode(const DexFile* pDexFile, u4 idx, u4 flags,
868 const DexFile::CodeItem* pCode, u4 codeOffset) {
869 fprintf(gOutFile, " registers : %d\n", pCode->registers_size_);
870 fprintf(gOutFile, " ins : %d\n", pCode->ins_size_);
871 fprintf(gOutFile, " outs : %d\n", pCode->outs_size_);
872 fprintf(gOutFile, " insns size : %d 16-bit code units\n",
873 pCode->insns_size_in_code_units_);
874
875 // Bytecode disassembly, if requested.
876 if (gOptions.disassemble) {
877 dumpBytecodes(pDexFile, idx, pCode, codeOffset);
878 }
879
880 // Try-catch blocks.
881 dumpCatches(pDexFile, pCode);
882
883 // Positions and locals table in the debug info.
884 bool is_static = (flags & kAccStatic) != 0;
885 fprintf(gOutFile, " positions : \n");
886 pDexFile->DecodeDebugInfo(
887 pCode, is_static, idx, dumpPositionsCb, nullptr, nullptr);
888 fprintf(gOutFile, " locals : \n");
889 pDexFile->DecodeDebugInfo(
890 pCode, is_static, idx, nullptr, dumpLocalsCb, nullptr);
891}
892
893/*
894 * Dumps a method.
895 */
896static void dumpMethod(const DexFile* pDexFile, u4 idx, u4 flags,
897 const DexFile::CodeItem* pCode, u4 codeOffset, int i) {
898 // Bail for anything private if export only requested.
899 if (gOptions.exportsOnly && (flags & (kAccPublic | kAccProtected)) == 0) {
900 return;
901 }
902
903 const DexFile::MethodId& pMethodId = pDexFile->GetMethodId(idx);
904 const char* name = pDexFile->StringDataByIdx(pMethodId.name_idx_);
905 const Signature signature = pDexFile->GetMethodSignature(pMethodId);
906 char* typeDescriptor = strdup(signature.ToString().c_str());
907 const char* backDescriptor = pDexFile->StringByTypeIdx(pMethodId.class_idx_);
908 char* accessStr = createAccessFlagStr(flags, kAccessForMethod);
909
910 if (gOptions.outputFormat == OUTPUT_PLAIN) {
911 fprintf(gOutFile, " #%d : (in %s)\n", i, backDescriptor);
912 fprintf(gOutFile, " name : '%s'\n", name);
913 fprintf(gOutFile, " type : '%s'\n", typeDescriptor);
914 fprintf(gOutFile, " access : 0x%04x (%s)\n", flags, accessStr);
915 if (pCode == nullptr) {
916 fprintf(gOutFile, " code : (none)\n");
917 } else {
918 fprintf(gOutFile, " code -\n");
919 dumpCode(pDexFile, idx, flags, pCode, codeOffset);
920 }
921 if (gOptions.disassemble) {
922 fputc('\n', gOutFile);
923 }
924 } else if (gOptions.outputFormat == OUTPUT_XML) {
925 const bool constructor = (name[0] == '<');
926
927 // Method name and prototype.
928 if (constructor) {
929 char* tmp = descriptorClassToDot(backDescriptor);
930 fprintf(gOutFile, "<constructor name=\"%s\"\n", tmp);
931 free(tmp);
932 tmp = descriptorToDot(backDescriptor);
933 fprintf(gOutFile, " type=\"%s\"\n", tmp);
934 free(tmp);
935 } else {
936 fprintf(gOutFile, "<method name=\"%s\"\n", name);
937 const char* returnType = strrchr(typeDescriptor, ')');
938 if (returnType == nullptr) {
939 fprintf(stderr, "bad method type descriptor '%s'\n", typeDescriptor);
940 goto bail;
941 }
942 char* tmp = descriptorToDot(returnType+1);
943 fprintf(gOutFile, " return=\"%s\"\n", tmp);
944 free(tmp);
945 fprintf(gOutFile, " abstract=%s\n", quotedBool((flags & kAccAbstract) != 0));
946 fprintf(gOutFile, " native=%s\n", quotedBool((flags & kAccNative) != 0));
947 fprintf(gOutFile, " synchronized=%s\n", quotedBool(
948 (flags & (kAccSynchronized | kAccDeclaredSynchronized)) != 0));
949 }
950
951 // Additional method flags.
952 fprintf(gOutFile, " static=%s\n", quotedBool((flags & kAccStatic) != 0));
953 fprintf(gOutFile, " final=%s\n", quotedBool((flags & kAccFinal) != 0));
954 // The "deprecated=" not knowable w/o parsing annotations.
955 fprintf(gOutFile, " visibility=%s\n>\n", quotedVisibility(flags));
956
957 // Parameters.
958 if (typeDescriptor[0] != '(') {
959 fprintf(stderr, "ERROR: bad descriptor '%s'\n", typeDescriptor);
960 goto bail;
961 }
962 char* tmpBuf = reinterpret_cast<char*>(malloc(strlen(typeDescriptor) + 1));
963 const char* base = typeDescriptor + 1;
964 int argNum = 0;
965 while (*base != ')') {
966 char* cp = tmpBuf;
967 while (*base == '[') {
968 *cp++ = *base++;
969 }
970 if (*base == 'L') {
971 // Copy through ';'.
972 do {
973 *cp = *base++;
974 } while (*cp++ != ';');
975 } else {
976 // Primitive char, copy it.
977 if (strchr("ZBCSIFJD", *base) == NULL) {
978 fprintf(stderr, "ERROR: bad method signature '%s'\n", base);
979 goto bail;
980 }
981 *cp++ = *base++;
982 }
983 // Null terminate and display.
984 *cp++ = '\0';
985 char* tmp = descriptorToDot(tmpBuf);
986 fprintf(gOutFile, "<parameter name=\"arg%d\" type=\"%s\">\n"
987 "</parameter>\n", argNum++, tmp);
988 free(tmp);
989 } // while
990 free(tmpBuf);
991 if (constructor) {
992 fprintf(gOutFile, "</constructor>\n");
993 } else {
994 fprintf(gOutFile, "</method>\n");
995 }
996 }
997
998 bail:
999 free(typeDescriptor);
1000 free(accessStr);
1001}
1002
1003/*
1004 * Dumps a static (class) field.
1005 */
1006static void dumpSField(const DexFile* pDexFile, u4 idx, u4 flags, int i) {
1007 // Bail for anything private if export only requested.
1008 if (gOptions.exportsOnly && (flags & (kAccPublic | kAccProtected)) == 0) {
1009 return;
1010 }
1011
1012 const DexFile::FieldId& pFieldId = pDexFile->GetFieldId(idx);
1013 const char* name = pDexFile->StringDataByIdx(pFieldId.name_idx_);
1014 const char* typeDescriptor = pDexFile->StringByTypeIdx(pFieldId.type_idx_);
1015 const char* backDescriptor = pDexFile->StringByTypeIdx(pFieldId.class_idx_);
1016 char* accessStr = createAccessFlagStr(flags, kAccessForField);
1017
1018 if (gOptions.outputFormat == OUTPUT_PLAIN) {
1019 fprintf(gOutFile, " #%d : (in %s)\n", i, backDescriptor);
1020 fprintf(gOutFile, " name : '%s'\n", name);
1021 fprintf(gOutFile, " type : '%s'\n", typeDescriptor);
1022 fprintf(gOutFile, " access : 0x%04x (%s)\n", flags, accessStr);
1023 } else if (gOptions.outputFormat == OUTPUT_XML) {
1024 fprintf(gOutFile, "<field name=\"%s\"\n", name);
1025 char *tmp = descriptorToDot(typeDescriptor);
1026 fprintf(gOutFile, " type=\"%s\"\n", tmp);
1027 free(tmp);
1028 fprintf(gOutFile, " transient=%s\n", quotedBool((flags & kAccTransient) != 0));
1029 fprintf(gOutFile, " volatile=%s\n", quotedBool((flags & kAccVolatile) != 0));
1030 // The "value=" is not knowable w/o parsing annotations.
1031 fprintf(gOutFile, " static=%s\n", quotedBool((flags & kAccStatic) != 0));
1032 fprintf(gOutFile, " final=%s\n", quotedBool((flags & kAccFinal) != 0));
1033 // The "deprecated=" is not knowable w/o parsing annotations.
1034 fprintf(gOutFile, " visibility=%s\n", quotedVisibility(flags));
1035 fprintf(gOutFile, ">\n</field>\n");
1036 }
1037
1038 free(accessStr);
1039}
1040
1041/*
1042 * Dumps an instance field.
1043 */
1044static void dumpIField(const DexFile* pDexFile, u4 idx, u4 flags, int i) {
1045 dumpSField(pDexFile, idx, flags, i);
1046}
1047
1048/*
1049 * Dumps the class.
1050 *
1051 * Note "idx" is a DexClassDef index, not a DexTypeId index.
1052 *
1053 * If "*pLastPackage" is nullptr or does not match the current class' package,
1054 * the value will be replaced with a newly-allocated string.
1055 */
1056static void dumpClass(const DexFile* pDexFile, int idx, char** pLastPackage) {
1057 const DexFile::ClassDef& pClassDef = pDexFile->GetClassDef(idx);
1058
1059 // Omitting non-public class.
1060 if (gOptions.exportsOnly && (pClassDef.access_flags_ & kAccPublic) == 0) {
1061 return;
1062 }
1063
1064 // For the XML output, show the package name. Ideally we'd gather
1065 // up the classes, sort them, and dump them alphabetically so the
1066 // package name wouldn't jump around, but that's not a great plan
1067 // for something that needs to run on the device.
1068 const char* classDescriptor = pDexFile->StringByTypeIdx(pClassDef.class_idx_);
1069 if (!(classDescriptor[0] == 'L' &&
1070 classDescriptor[strlen(classDescriptor)-1] == ';')) {
1071 // Arrays and primitives should not be defined explicitly. Keep going?
1072 fprintf(stderr, "Malformed class name '%s'\n", classDescriptor);
1073 } else if (gOptions.outputFormat == OUTPUT_XML) {
1074 char* mangle = strdup(classDescriptor + 1);
1075 mangle[strlen(mangle)-1] = '\0';
1076
1077 // Reduce to just the package name.
1078 char* lastSlash = strrchr(mangle, '/');
1079 if (lastSlash != nullptr) {
1080 *lastSlash = '\0';
1081 } else {
1082 *mangle = '\0';
1083 }
1084
1085 for (char* cp = mangle; *cp != '\0'; cp++) {
1086 if (*cp == '/') {
1087 *cp = '.';
1088 }
1089 } // for
1090
1091 if (*pLastPackage == nullptr || strcmp(mangle, *pLastPackage) != 0) {
1092 // Start of a new package.
1093 if (*pLastPackage != nullptr) {
1094 fprintf(gOutFile, "</package>\n");
1095 }
1096 fprintf(gOutFile, "<package name=\"%s\"\n>\n", mangle);
1097 free(*pLastPackage);
1098 *pLastPackage = mangle;
1099 } else {
1100 free(mangle);
1101 }
1102 }
1103
1104 // General class information.
1105 char* accessStr = createAccessFlagStr(pClassDef.access_flags_, kAccessForClass);
1106 const char* superclassDescriptor;
1107 if (pClassDef.superclass_idx_ == DexFile::kDexNoIndex16) {
1108 superclassDescriptor = nullptr;
1109 } else {
1110 superclassDescriptor = pDexFile->StringByTypeIdx(pClassDef.superclass_idx_);
1111 }
1112 if (gOptions.outputFormat == OUTPUT_PLAIN) {
1113 fprintf(gOutFile, "Class #%d -\n", idx);
1114 fprintf(gOutFile, " Class descriptor : '%s'\n", classDescriptor);
1115 fprintf(gOutFile, " Access flags : 0x%04x (%s)\n", pClassDef.access_flags_, accessStr);
1116 if (superclassDescriptor != nullptr) {
1117 fprintf(gOutFile, " Superclass : '%s'\n", superclassDescriptor);
1118 }
1119 fprintf(gOutFile, " Interfaces -\n");
1120 } else {
1121 char* tmp = descriptorClassToDot(classDescriptor);
1122 fprintf(gOutFile, "<class name=\"%s\"\n", tmp);
1123 free(tmp);
1124 if (superclassDescriptor != nullptr) {
1125 tmp = descriptorToDot(superclassDescriptor);
1126 fprintf(gOutFile, " extends=\"%s\"\n", tmp);
1127 free(tmp);
1128 }
1129 fprintf(gOutFile, " abstract=%s\n", quotedBool((pClassDef.access_flags_ & kAccAbstract) != 0));
1130 fprintf(gOutFile, " static=%s\n", quotedBool((pClassDef.access_flags_ & kAccStatic) != 0));
1131 fprintf(gOutFile, " final=%s\n", quotedBool((pClassDef.access_flags_ & kAccFinal) != 0));
1132 // The "deprecated=" not knowable w/o parsing annotations.
1133 fprintf(gOutFile, " visibility=%s\n", quotedVisibility(pClassDef.access_flags_));
1134 fprintf(gOutFile, ">\n");
1135 }
1136
1137 // Interfaces.
1138 const DexFile::TypeList* pInterfaces = pDexFile->GetInterfacesList(pClassDef);
1139 if (pInterfaces != nullptr) {
1140 for (u4 i = 0; i < pInterfaces->Size(); i++) {
1141 dumpInterface(pDexFile, pInterfaces->GetTypeItem(i), i);
1142 } // for
1143 }
1144
1145 // Fields and methods.
1146 const u1* pEncodedData = pDexFile->GetClassData(pClassDef);
1147 if (pEncodedData == nullptr) {
1148 if (gOptions.outputFormat == OUTPUT_PLAIN) {
1149 fprintf(gOutFile, " Static fields -\n");
1150 fprintf(gOutFile, " Instance fields -\n");
1151 fprintf(gOutFile, " Direct methods -\n");
1152 fprintf(gOutFile, " Virtual methods -\n");
1153 }
1154 } else {
1155 ClassDataItemIterator pClassData(*pDexFile, pEncodedData);
1156 if (gOptions.outputFormat == OUTPUT_PLAIN) {
1157 fprintf(gOutFile, " Static fields -\n");
1158 }
1159 for (int i = 0; pClassData.HasNextStaticField(); i++, pClassData.Next()) {
1160 dumpSField(pDexFile, pClassData.GetMemberIndex(),
1161 pClassData.GetRawMemberAccessFlags(), i);
1162 } // for
1163 if (gOptions.outputFormat == OUTPUT_PLAIN) {
1164 fprintf(gOutFile, " Instance fields -\n");
1165 }
1166 for (int i = 0; pClassData.HasNextInstanceField(); i++, pClassData.Next()) {
1167 dumpIField(pDexFile, pClassData.GetMemberIndex(),
1168 pClassData.GetRawMemberAccessFlags(), i);
1169 } // for
1170 if (gOptions.outputFormat == OUTPUT_PLAIN) {
1171 fprintf(gOutFile, " Direct methods -\n");
1172 }
1173 for (int i = 0; pClassData.HasNextDirectMethod(); i++, pClassData.Next()) {
1174 dumpMethod(pDexFile, pClassData.GetMemberIndex(),
1175 pClassData.GetRawMemberAccessFlags(),
1176 pClassData.GetMethodCodeItem(),
1177 pClassData.GetMethodCodeItemOffset(), i);
1178 } // for
1179 if (gOptions.outputFormat == OUTPUT_PLAIN) {
1180 fprintf(gOutFile, " Virtual methods -\n");
1181 }
1182 for (int i = 0; pClassData.HasNextVirtualMethod(); i++, pClassData.Next()) {
1183 dumpMethod(pDexFile, pClassData.GetMemberIndex(),
1184 pClassData.GetRawMemberAccessFlags(),
1185 pClassData.GetMethodCodeItem(),
1186 pClassData.GetMethodCodeItemOffset(), i);
1187 } // for
1188 }
1189
1190 // End of class.
1191 if (gOptions.outputFormat == OUTPUT_PLAIN) {
1192 const char* fileName;
1193 if (pClassDef.source_file_idx_ != DexFile::kDexNoIndex) {
1194 fileName = pDexFile->StringDataByIdx(pClassDef.source_file_idx_);
1195 } else {
1196 fileName = "unknown";
1197 }
1198 fprintf(gOutFile, " source_file_idx : %d (%s)\n\n",
1199 pClassDef.source_file_idx_, fileName);
1200 } else if (gOptions.outputFormat == OUTPUT_XML) {
1201 fprintf(gOutFile, "</class>\n");
1202 }
1203
1204 free(accessStr);
1205}
1206
1207/*
1208 * Dumps the requested sections of the file.
1209 */
1210static void processDexFile(const char* fileName, const DexFile* pDexFile) {
1211 if (gOptions.verbose) {
1212 fprintf(gOutFile, "Opened '%s', DEX version '%.3s'\n",
1213 fileName, pDexFile->GetHeader().magic_ + 4);
1214 }
1215
1216 // Headers.
1217 if (gOptions.showFileHeaders) {
1218 dumpFileHeader(pDexFile);
1219 }
1220
1221 // Open XML context.
1222 if (gOptions.outputFormat == OUTPUT_XML) {
1223 fprintf(gOutFile, "<api>\n");
1224 }
1225
1226 // Iterate over all classes.
1227 char* package = nullptr;
1228 const u4 classDefsSize = pDexFile->GetHeader().class_defs_size_;
1229 for (u4 i = 0; i < classDefsSize; i++) {
1230 if (gOptions.showSectionHeaders) {
1231 dumpClassDef(pDexFile, i);
1232 }
1233 dumpClass(pDexFile, i, &package);
1234 } // for
1235
1236 // Free the last package allocated.
1237 if (package != nullptr) {
1238 fprintf(gOutFile, "</package>\n");
1239 free(package);
1240 }
1241
1242 // Close XML context.
1243 if (gOptions.outputFormat == OUTPUT_XML) {
1244 fprintf(gOutFile, "</api>\n");
1245 }
1246}
1247
1248/*
1249 * Processes a single file (either direct .dex or indirect .zip/.jar/.apk).
1250 */
1251int processFile(const char* fileName) {
1252 if (gOptions.verbose) {
1253 fprintf(gOutFile, "Processing '%s'...\n", fileName);
1254 }
1255
1256 // If the file is not a .dex file, the function tries .zip/.jar/.apk files,
1257 // all of which are Zip archives with "classes.dex" inside. The compressed
1258 // data needs to be extracted to a temp file, the location of which varies.
1259 //
1260 // TODO(ajcbik): fix following issues
1261 //
1262 // (1) gOptions.tempFileName is not accounted for
1263 // (2) gOptions.ignoreBadChecksum is not accounted for
1264 //
1265 std::string error_msg;
1266 std::vector<std::unique_ptr<const DexFile>> dex_files;
1267 if (!DexFile::Open(fileName, fileName, &error_msg, &dex_files)) {
1268 // Display returned error message to user. Note that this error behavior
1269 // differs from the error messages shown by the original Dalvik dexdump.
1270 fputs(error_msg.c_str(), stderr);
1271 fputc('\n', stderr);
1272 return -1;
1273 }
1274
Aart Bik4e149602015-07-09 11:45:28 -07001275 // Success. Either report checksum verification or process
1276 // all dex files found in given file.
Aart Bik69ae54a2015-07-01 14:52:26 -07001277 if (gOptions.checksumOnly) {
1278 fprintf(gOutFile, "Checksum verified\n");
1279 } else {
Aart Bik4e149602015-07-09 11:45:28 -07001280 for (size_t i = 0; i < dex_files.size(); i++) {
1281 processDexFile(fileName, dex_files[i].get());
1282 }
Aart Bik69ae54a2015-07-01 14:52:26 -07001283 }
1284 return 0;
1285}
1286
1287} // namespace art