blob: a4d97eb7b6a92fa6eb2b760c217a01564da0f68a [file] [log] [blame]
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* The "dexdump" tool is intended to mimic "objdump". When possible, use
* similar command-line arguments.
*
* TODO: rework the output format to be more regexp-friendly
*/
#include "libdex/DexFile.h"
#include "libdex/DexCatch.h"
#include "libdex/DexClass.h"
#include "libdex/DexProto.h"
#include "libdex/InstrUtils.h"
#include "libdex/SysUtil.h"
#include "libdex/CmdUtils.h"
#include "dexdump/OpCodeNames.h"
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <errno.h>
#include <assert.h>
static const char* gProgName = "dexdump";
static InstructionWidth* gInstrWidth;
static InstructionFormat* gInstrFormat;
/* command-line options */
struct {
bool disassemble;
bool showFileHeaders;
bool showSectionHeaders;
const char* tempFileName;
} gOptions;
/* basic info about a field or method */
typedef struct FieldMethodInfo {
const char* classDescriptor;
const char* name;
const char* signature;
} FieldMethodInfo;
/*
* Get 2 little-endian bytes.
*/
static inline u2 get2LE(unsigned char const* pSrc)
{
return pSrc[0] | (pSrc[1] << 8);
}
/*
* Return a newly-allocated string for the "dot version" of the class
* name for the given type descriptor. That is, The initial "L" and
* final ";" (if any) have been removed and all occurrences of '/'
* have been changed to '.'.
*/
static char* descriptorToDot(const char* str)
{
size_t at = strlen(str);
char* newStr;
if (str[0] == 'L') {
assert(str[at - 1] == ';');
at -= 2; /* Two fewer chars to copy. */
str++; /* Skip the 'L'. */
}
newStr = malloc(at + 1); /* Add one for the '\0'. */
newStr[at] = '\0';
while (at > 0) {
at--;
newStr[at] = (str[at] == '/') ? '.' : str[at];
}
return newStr;
}
/*
* Count the number of '1' bits in a word.
*
* Having completed this, I'm ready for an interview at Google.
*
* TODO? there's a parallel version w/o loops. Performance not currently
* important.
*/
static int countOnes(u4 val)
{
int count = 0;
while (val != 0) {
val &= val-1;
count++;
}
return count;
}
/*
* Flag for use with createAccessFlagStr().
*/
typedef enum AccessFor {
kAccessForClass = 0, kAccessForMethod = 1, kAccessForField = 2,
kAccessForMAX
} AccessFor;
/*
* Create a new string with human-readable access flags.
*
* In the base language the access_flags fields are type u2; in Dalvik
* they're u4.
*/
static char* createAccessFlagStr(u4 flags, AccessFor forWhat)
{
#define NUM_FLAGS 18
static const char* kAccessStrings[kAccessForMAX][NUM_FLAGS] = {
{
/* class, inner class */
"PUBLIC", /* 0x0001 */
"PRIVATE", /* 0x0002 */
"PROTECTED", /* 0x0004 */
"STATIC", /* 0x0008 */
"FINAL", /* 0x0010 */
"?", /* 0x0020 */
"?", /* 0x0040 */
"?", /* 0x0080 */
"?", /* 0x0100 */
"INTERFACE", /* 0x0200 */
"ABSTRACT", /* 0x0400 */
"?", /* 0x0800 */
"SYNTHETIC", /* 0x1000 */
"ANNOTATION", /* 0x2000 */
"ENUM", /* 0x4000 */
"?", /* 0x8000 */
"VERIFIED", /* 0x10000 */
"OPTIMIZED", /* 0x20000 */
},
{
/* method */
"PUBLIC", /* 0x0001 */
"PRIVATE", /* 0x0002 */
"PROTECTED", /* 0x0004 */
"STATIC", /* 0x0008 */
"FINAL", /* 0x0010 */
"SYNCHRONIZED", /* 0x0020 */
"BRIDGE", /* 0x0040 */
"VARARGS", /* 0x0080 */
"NATIVE", /* 0x0100 */
"?", /* 0x0200 */
"ABSTRACT", /* 0x0400 */
"STRICT", /* 0x0800 */
"SYNTHETIC", /* 0x1000 */
"?", /* 0x2000 */
"?", /* 0x4000 */
"MIRANDA", /* 0x8000 */
"CONSTRUCTOR", /* 0x10000 */
"DECLARED_SYNCHRONIZED", /* 0x20000 */
},
{
/* field */
"PUBLIC", /* 0x0001 */
"PRIVATE", /* 0x0002 */
"PROTECTED", /* 0x0004 */
"STATIC", /* 0x0008 */
"FINAL", /* 0x0010 */
"?", /* 0x0020 */
"VOLATILE", /* 0x0040 */
"TRANSIENT", /* 0x0080 */
"?", /* 0x0100 */
"?", /* 0x0200 */
"?", /* 0x0400 */
"?", /* 0x0800 */
"SYNTHETIC", /* 0x1000 */
"?", /* 0x2000 */
"ENUM", /* 0x4000 */
"?", /* 0x8000 */
"?", /* 0x10000 */
"?", /* 0x20000 */
},
};
const int kLongest = 21; /* strlen of longest string above */
int i, count;
char* str;
char* cp;
/*
* Allocate enough storage to hold the expected number of strings,
* plus a space between each. We over-allocate, using the longest
* string above as the base metric.
*/
count = countOnes(flags);
cp = str = (char*) malloc(count * (kLongest+1) +1);
for (i = 0; i < NUM_FLAGS; i++) {
if (flags & 0x01) {
const char* accessStr = kAccessStrings[forWhat][i];
int len = strlen(accessStr);
if (cp != str)
*cp++ = ' ';
memcpy(cp, accessStr, len);
cp += len;
}
flags >>= 1;
}
*cp = '\0';
return str;
}
/*
* Dump the file header.
*/
void dumpFileHeader(const DexFile* pDexFile)
{
const DexHeader* pHeader = pDexFile->pHeader;
printf("DEX file header:\n");
printf("magic : '%.8s'\n", pHeader->magic);
printf("checksum : %08x\n", pHeader->checksum);
printf("signature : %02x%02x...%02x%02x\n",
pHeader->signature[0], pHeader->signature[1],
pHeader->signature[kSHA1DigestLen-2],
pHeader->signature[kSHA1DigestLen-1]);
printf("file_size : %d\n", pHeader->fileSize);
printf("header_size : %d\n", pHeader->headerSize);
printf("link_size : %d\n", pHeader->linkSize);
printf("link_off : %d (0x%06x)\n",
pHeader->linkOff, pHeader->linkOff);
printf("string_ids_size : %d\n", pHeader->stringIdsSize);
printf("string_ids_off : %d (0x%06x)\n",
pHeader->stringIdsOff, pHeader->stringIdsOff);
printf("type_ids_size : %d\n", pHeader->typeIdsSize);
printf("type_ids_off : %d (0x%06x)\n",
pHeader->typeIdsOff, pHeader->typeIdsOff);
printf("field_ids_size : %d\n", pHeader->fieldIdsSize);
printf("field_ids_off : %d (0x%06x)\n",
pHeader->fieldIdsOff, pHeader->fieldIdsOff);
printf("method_ids_size : %d\n", pHeader->methodIdsSize);
printf("method_ids_off : %d (0x%06x)\n",
pHeader->methodIdsOff, pHeader->methodIdsOff);
printf("class_defs_size : %d\n", pHeader->classDefsSize);
printf("class_defs_off : %d (0x%06x)\n",
pHeader->classDefsOff, pHeader->classDefsOff);
printf("data_size : %d\n", pHeader->dataSize);
printf("data_off : %d (0x%06x)\n",
pHeader->dataOff, pHeader->dataOff);
printf("\n");
}
/*
* Dump a class_def_item.
*/
void dumpClassDef(DexFile* pDexFile, int idx)
{
const DexClassDef* pClassDef;
const u1* pEncodedData;
DexClassData* pClassData;
pClassDef = dexGetClassDef(pDexFile, idx);
pEncodedData = dexGetClassData(pDexFile, pClassDef);
pClassData = dexReadAndVerifyClassData(&pEncodedData, NULL);
if (pClassData == NULL) {
fprintf(stderr, "Trouble reading class data\n");
return;
}
printf("Class #%d header:\n", idx);
printf("class_idx : %d\n", pClassDef->classIdx);
printf("access_flags : %d (0x%04x)\n",
pClassDef->accessFlags, pClassDef->accessFlags);
printf("superclass_idx : %d\n", pClassDef->superclassIdx);
printf("interfaces_off : %d (0x%06x)\n",
pClassDef->interfacesOff, pClassDef->interfacesOff);
printf("source_file_idx : %d\n", pClassDef->sourceFileIdx);
printf("annotations_off : %d (0x%06x)\n",
pClassDef->annotationsOff, pClassDef->annotationsOff);
printf("class_data_off : %d (0x%06x)\n",
pClassDef->classDataOff, pClassDef->classDataOff);
printf("static_fields_size : %d\n", pClassData->header.staticFieldsSize);
printf("instance_fields_size: %d\n",
pClassData->header.instanceFieldsSize);
printf("direct_methods_size : %d\n", pClassData->header.directMethodsSize);
printf("virtual_methods_size: %d\n",
pClassData->header.virtualMethodsSize);
printf("\n");
free(pClassData);
}
/*
* Dump an interface.
*/
void dumpInterface(const DexFile* pDexFile, const DexTypeItem* pTypeItem,
int i)
{
const char* interfaceName =
dexStringByTypeIdx(pDexFile, pTypeItem->typeIdx);
printf(" #%d : '%s'\n", i, interfaceName);
}
/*
* Dump the catches table associated with the code.
*/
void dumpCatches(DexFile* pDexFile, const DexCode* pCode)
{
u4 triesSize = pCode->triesSize;
if (triesSize == 0) {
printf(" catches : (none)\n");
return;
}
printf(" catches : %d\n", triesSize);
const DexTry* pTries = dexGetTries(pCode);
u4 i;
for (i = 0; i < triesSize; i++) {
const DexTry* pTry = &pTries[i];
u4 start = pTry->startAddr;
u4 end = start + pTry->insnCount;
DexCatchIterator iterator;
printf(" 0x%04x - 0x%04x\n", start, end);
dexCatchIteratorInit(&iterator, pCode, pTry->handlerOff);
for (;;) {
DexCatchHandler* handler = dexCatchIteratorNext(&iterator);
const char* descriptor;
if (handler == NULL) {
break;
}
descriptor = (handler->typeIdx == kDexNoIndex) ? "<any>" :
dexStringByTypeIdx(pDexFile, handler->typeIdx);
printf(" %s -> 0x%04x\n", descriptor,
handler->address);
}
}
}
static int dumpPositionsCb(void *cnxt, u4 address, u4 lineNum)
{
printf(" 0x%04x line=%d\n", address, lineNum);
return 0;
}
/*
* Dump the positions list.
*/
void dumpPositions(DexFile* pDexFile, const DexCode* pCode,
const DexMethod *pDexMethod)
{
printf(" positions : \n");
const DexMethodId *pMethodId
= dexGetMethodId(pDexFile, pDexMethod->methodIdx);
const char *classDescriptor
= dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
dexDecodeDebugInfo(pDexFile, pCode, classDescriptor, pMethodId->protoIdx,
pDexMethod->accessFlags, dumpPositionsCb, NULL, NULL);
}
static void dumpLocalsCb(void *cnxt, u2 reg, u4 startAddress,
u4 endAddress, const char *name, const char *descriptor,
const char *signature)
{
printf(" 0x%04x - 0x%04x reg=%d %s %s %s\n",
startAddress, endAddress, reg, name, descriptor,
signature);
}
/*
* Dump the locals list.
*/
void dumpLocals(DexFile* pDexFile, const DexCode* pCode,
const DexMethod *pDexMethod)
{
printf(" locals : \n");
const DexMethodId *pMethodId
= dexGetMethodId(pDexFile, pDexMethod->methodIdx);
const char *classDescriptor
= dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
dexDecodeDebugInfo(pDexFile, pCode, classDescriptor, pMethodId->protoIdx,
pDexMethod->accessFlags, NULL, dumpLocalsCb, NULL);
}
/*
* Get information about a method.
*/
bool getMethodInfo(DexFile* pDexFile, u4 methodIdx, FieldMethodInfo* pMethInfo)
{
const DexMethodId* pMethodId;
if (methodIdx >= pDexFile->pHeader->methodIdsSize)
return false;
pMethodId = dexGetMethodId(pDexFile, methodIdx);
pMethInfo->name = dexStringById(pDexFile, pMethodId->nameIdx);
pMethInfo->signature = dexCopyDescriptorFromMethodId(pDexFile, pMethodId);
pMethInfo->classDescriptor =
dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
return true;
}
/*
* Get information about a field.
*/
bool getFieldInfo(DexFile* pDexFile, u4 fieldIdx, FieldMethodInfo* pFieldInfo)
{
const DexFieldId* pFieldId;
if (fieldIdx >= pDexFile->pHeader->fieldIdsSize)
return false;
pFieldId = dexGetFieldId(pDexFile, fieldIdx);
pFieldInfo->name = dexStringById(pDexFile, pFieldId->nameIdx);
pFieldInfo->signature = dexStringByTypeIdx(pDexFile, pFieldId->typeIdx);
pFieldInfo->classDescriptor =
dexStringByTypeIdx(pDexFile, pFieldId->classIdx);
return true;
}
/*
* Look up a class' descriptor.
*/
const char* getClassDescriptor(DexFile* pDexFile, u4 classIdx)
{
return dexStringByTypeIdx(pDexFile, classIdx);
}
/*
* Dump a single instruction.
*/
void dumpInstruction(DexFile* pDexFile, const DexCode* pCode, int insnIdx,
int insnWidth, const DecodedInstruction* pDecInsn)
{
static const float gSpecialTab[16] = {
-2.0f, -1.0f, -0.5f, -0.25f, -0.1f, 0.1f, 0.25f, 0.5f,
1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 10.0f, 100.0f, 1000.0f
};
const u2* insns = pCode->insns;
int i;
printf("%06x:", ((u1*)insns - pDexFile->baseAddr) + insnIdx*2);
for (i = 0; i < 8; i++) {
if (i < insnWidth) {
if (i == 7) {
printf(" ... ");
} else {
/* print 16-bit value in little-endian order */
const u1* bytePtr = (const u1*) &insns[insnIdx+i];
printf(" %02x%02x", bytePtr[0], bytePtr[1]);
}
} else {
fputs(" ", stdout);
}
}
if (pDecInsn->opCode == OP_NOP) {
u2 instr = get2LE((const u1*) &insns[insnIdx]);
if (instr == kPackedSwitchSignature) {
printf("|%04x: packed-switch-data (%d units)",
insnIdx, insnWidth);
} else if (instr == kSparseSwitchSignature) {
printf("|%04x: sparse-switch-data (%d units)",
insnIdx, insnWidth);
} else if (instr == kArrayDataSignature) {
printf("|%04x: array-data (%d units)",
insnIdx, insnWidth);
} else {
printf("|%04x: nop // spacer", insnIdx);
}
} else {
printf("|%04x: %s", insnIdx, getOpcodeName(pDecInsn->opCode));
}
switch (dexGetInstrFormat(gInstrFormat, pDecInsn->opCode)) {
case kFmt10x: // op
break;
case kFmt12x: // op vA, vB
printf(" v%d, v%d", pDecInsn->vA, pDecInsn->vB);
break;
case kFmt11n: // op vA, #+B
printf(" v%d, #int %d // #%x",
pDecInsn->vA, (s4)pDecInsn->vB, (u1)pDecInsn->vB);
break;
case kFmt11x: // op vAA
printf(" v%d", pDecInsn->vA);
break;
case kFmt10t: // op +AA
case kFmt20t: // op +AAAA
{
s4 targ = (s4) pDecInsn->vA;
printf(" %04x // %c%04x",
insnIdx + targ,
(targ < 0) ? '-' : '+',
(targ < 0) ? -targ : targ);
}
break;
case kFmt22x: // op vAA, vBBBB
printf(" v%d, v%d", pDecInsn->vA, pDecInsn->vB);
break;
case kFmt21t: // op vAA, +BBBB
{
s4 targ = (s4) pDecInsn->vB;
printf(" v%d, %04x // %c%04x", pDecInsn->vA,
insnIdx + targ,
(targ < 0) ? '-' : '+',
(targ < 0) ? -targ : targ);
}
break;
case kFmt21s: // op vAA, #+BBBB
printf(" v%d, #int %d // #%x",
pDecInsn->vA, (s4)pDecInsn->vB, (u2)pDecInsn->vB);
break;
case kFmt21h: // op vAA, #+BBBB0000[00000000]
// The printed format varies a bit based on the actual opcode.
if (pDecInsn->opCode == OP_CONST_HIGH16) {
s4 value = pDecInsn->vB << 16;
printf(" v%d, #int %d // #%x",
pDecInsn->vA, value, (u2)pDecInsn->vB);
} else {
s8 value = ((s8) pDecInsn->vB) << 48;
printf(" v%d, #long %lld // #%x",
pDecInsn->vA, value, (u2)pDecInsn->vB);
}
break;
case kFmt21c: // op vAA, thing@BBBB
if (pDecInsn->opCode == OP_CONST_STRING) {
printf(" v%d, \"%s\" // string@%04x", pDecInsn->vA,
dexStringById(pDexFile, pDecInsn->vB), pDecInsn->vB);
} else if (pDecInsn->opCode == OP_CHECK_CAST ||
pDecInsn->opCode == OP_NEW_INSTANCE ||
pDecInsn->opCode == OP_CONST_CLASS)
{
printf(" v%d, %s // class@%04x", pDecInsn->vA,
getClassDescriptor(pDexFile, pDecInsn->vB), pDecInsn->vB);
} else /* OP_SGET* */ {
FieldMethodInfo fieldInfo;
if (getFieldInfo(pDexFile, pDecInsn->vB, &fieldInfo)) {
printf(" v%d, %s.%s:%s // field@%04x", pDecInsn->vA,
fieldInfo.classDescriptor, fieldInfo.name,
fieldInfo.signature, pDecInsn->vB);
} else {
printf(" v%d, ??? // field@%04x", pDecInsn->vA, pDecInsn->vB);
}
}
break;
case kFmt23x: // op vAA, vBB, vCC
printf(" v%d, v%d, v%d", pDecInsn->vA, pDecInsn->vB, pDecInsn->vC);
break;
case kFmt22b: // op vAA, vBB, #+CC
printf(" v%d, v%d, #int %d // #%02x",
pDecInsn->vA, pDecInsn->vB, (s4)pDecInsn->vC, (u1)pDecInsn->vC);
break;
case kFmt22t: // op vA, vB, +CCCC
{
s4 targ = (s4) pDecInsn->vC;
printf(" v%d, v%d, %04x // %c%04x", pDecInsn->vA, pDecInsn->vB,
insnIdx + targ,
(targ < 0) ? '-' : '+',
(targ < 0) ? -targ : targ);
}
break;
case kFmt22s: // op vA, vB, #+CCCC
printf(" v%d, v%d, #int %d // #%04x",
pDecInsn->vA, pDecInsn->vB, (s4)pDecInsn->vC, (u2)pDecInsn->vC);
break;
case kFmt22c: // op vA, vB, thing@CCCC
if (pDecInsn->opCode >= OP_IGET && pDecInsn->opCode <= OP_IPUT_SHORT) {
FieldMethodInfo fieldInfo;
if (getFieldInfo(pDexFile, pDecInsn->vC, &fieldInfo)) {
printf(" v%d, v%d, %s.%s:%s // field@%04x", pDecInsn->vA,
pDecInsn->vB, fieldInfo.classDescriptor, fieldInfo.name,
fieldInfo.signature, pDecInsn->vC);
} else {
printf(" v%d, v%d, ??? // field@%04x", pDecInsn->vA,
pDecInsn->vB, pDecInsn->vC);
}
} else {
printf(" v%d, v%d, %s // class@%04x",
pDecInsn->vA, pDecInsn->vB,
getClassDescriptor(pDexFile, pDecInsn->vC), pDecInsn->vC);
}
break;
case kFmt22cs: // [opt] op vA, vB, field offset CCCC
printf(" v%d, v%d, [obj+%04x]",
pDecInsn->vA, pDecInsn->vB, pDecInsn->vC);
break;
case kFmt30t:
printf(" #%08x", pDecInsn->vA);
break;
case kFmt31i: // op vAA, #+BBBBBBBB
{
/* this is often, but not always, a float */
union {
float f;
u4 i;
} conv;
conv.i = pDecInsn->vB;
printf(" v%d, #float %f // #%08x",
pDecInsn->vA, conv.f, pDecInsn->vB);
}
break;
case kFmt31c: // op vAA, thing@BBBBBBBB
printf(" v%d, \"%s\" // string@%08x", pDecInsn->vA,
dexStringById(pDexFile, pDecInsn->vB), pDecInsn->vB);
break;
case kFmt31t: // op vAA, offset +BBBBBBBB
printf(" v%d, %08x // +%08x",
pDecInsn->vA, insnIdx + pDecInsn->vB, pDecInsn->vB);
break;
case kFmt32x: // op vAAAA, vBBBB
printf(" v%d, v%d", pDecInsn->vA, pDecInsn->vB);
break;
case kFmt35c: // op vB, {vD, vE, vF, vG, vA}, thing@CCCC
{
/* NOTE: decoding of 35c doesn't quite match spec */
fputs(" {", stdout);
for (i = 0; i < (int) pDecInsn->vA; i++) {
if (i == 0)
printf("v%d", pDecInsn->arg[i]);
else
printf(", v%d", pDecInsn->arg[i]);
}
if (pDecInsn->opCode == OP_FILLED_NEW_ARRAY) {
printf("}, %s // class@%04x",
getClassDescriptor(pDexFile, pDecInsn->vB), pDecInsn->vB);
} else {
FieldMethodInfo methInfo;
if (getMethodInfo(pDexFile, pDecInsn->vB, &methInfo)) {
printf("}, %s.%s:%s // method@%04x",
methInfo.classDescriptor, methInfo.name,
methInfo.signature, pDecInsn->vB);
} else {
printf("}, ??? // method@%04x", pDecInsn->vB);
}
}
}
break;
case kFmt35ms: // [opt] invoke-virtual+super
case kFmt35fs: // [opt] invoke-interface
{
fputs(" {", stdout);
for (i = 0; i < (int) pDecInsn->vA; i++) {
if (i == 0)
printf("v%d", pDecInsn->arg[i]);
else
printf(", v%d", pDecInsn->arg[i]);
}
printf("}, [%04x] // vtable #%04x", pDecInsn->vB, pDecInsn->vB);
}
break;
case kFmt3rc: // op {vCCCC .. v(CCCC+AA-1)}, meth@BBBB
{
/*
* This doesn't match the "dx" output when some of the args are
* 64-bit values -- dx only shows the first register.
*/
fputs(" {", stdout);
for (i = 0; i < (int) pDecInsn->vA; i++) {
if (i == 0)
printf("v%d", pDecInsn->vC + i);
else
printf(", v%d", pDecInsn->vC + i);
}
if (pDecInsn->opCode == OP_FILLED_NEW_ARRAY_RANGE) {
printf("}, %s // class@%04x",
getClassDescriptor(pDexFile, pDecInsn->vB), pDecInsn->vB);
} else {
FieldMethodInfo methInfo;
if (getMethodInfo(pDexFile, pDecInsn->vB, &methInfo)) {
printf("}, %s.%s:%s // method@%04x",
methInfo.classDescriptor, methInfo.name,
methInfo.signature, pDecInsn->vB);
} else {
printf("}, ??? // method@%04x", pDecInsn->vB);
}
}
}
break;
case kFmt3rms: // [opt] invoke-virtual+super/range
case kFmt3rfs: // [opt] invoke-interface/range
{
/*
* This doesn't match the "dx" output when some of the args are
* 64-bit values -- dx only shows the first register.
*/
fputs(" {", stdout);
for (i = 0; i < (int) pDecInsn->vA; i++) {
if (i == 0)
printf("v%d", pDecInsn->vC + i);
else
printf(", v%d", pDecInsn->vC + i);
}
printf("}, [%04x] // vtable #%04x", pDecInsn->vB, pDecInsn->vB);
}
break;
case kFmt3inline: // [opt] inline invoke
{
#if 0
const InlineOperation* inlineOpsTable = dvmGetInlineOpsTable();
u4 tableLen = dvmGetInlineOpsTableLength();
#endif
fputs(" {", stdout);
for (i = 0; i < (int) pDecInsn->vA; i++) {
if (i == 0)
printf("v%d", pDecInsn->arg[i]);
else
printf(", v%d", pDecInsn->arg[i]);
}
#if 0
if (pDecInsn->vB < tableLen) {
printf("}, %s.%s:%s // inline #%04x",
inlineOpsTable[pDecInsn->vB].classDescriptor,
inlineOpsTable[pDecInsn->vB].methodName,
inlineOpsTable[pDecInsn->vB].methodSignature,
pDecInsn->vB);
} else {
#endif
printf("}, [%04x] // inline #%04x", pDecInsn->vB, pDecInsn->vB);
#if 0
}
#endif
}
break;
case kFmt51l: // op vAA, #+BBBBBBBBBBBBBBBB
{
/* this is often, but not always, a double */
union {
double d;
u8 j;
} conv;
conv.j = pDecInsn->vB_wide;
printf(" v%d, #double %f // #%016llx",
pDecInsn->vA, conv.d, pDecInsn->vB_wide);
}
break;
case kFmtUnknown:
break;
default:
printf(" ???");
break;
}
putchar('\n');
}
/*
* Dump a bytecode disassembly.
*/
void dumpBytecodes(DexFile* pDexFile, const DexMethod* pDexMethod)
{
const DexCode* pCode = dexGetCode(pDexFile, pDexMethod);
const u2* insns;
int insnIdx;
FieldMethodInfo methInfo;
int startAddr;
char* className = NULL;
assert(pCode->insnsSize > 0);
insns = pCode->insns;
getMethodInfo(pDexFile, pDexMethod->methodIdx, &methInfo);
startAddr = ((u1*)pCode - pDexFile->baseAddr);
className = descriptorToDot(methInfo.classDescriptor);
printf("%06x: |[%06x] %s.%s:%s\n",
startAddr, startAddr,
className, methInfo.name, methInfo.signature);
insnIdx = 0;
while (insnIdx < (int) pCode->insnsSize) {
int insnWidth;
OpCode opCode;
DecodedInstruction decInsn;
u2 instr;
instr = get2LE((const u1*)insns);
if (instr == kPackedSwitchSignature) {
insnWidth = 4 + get2LE((const u1*)(insns+1)) * 2;
} else if (instr == kSparseSwitchSignature) {
insnWidth = 2 + get2LE((const u1*)(insns+1)) * 4;
} else if (instr == kArrayDataSignature) {
int width = get2LE((const u1*)(insns+1));
int size = get2LE((const u1*)(insns+2)) |
(get2LE((const u1*)(insns+3))<<16);
// The plus 1 is to round up for odd size and width
insnWidth = 4 + ((size * width) + 1) / 2;
} else {
opCode = instr & 0xff;
insnWidth = dexGetInstrWidthAbs(gInstrWidth, opCode);
if (insnWidth == 0) {
fprintf(stderr,
"GLITCH: zero-width instruction at idx=0x%04x\n", insnIdx);
break;
}
}
dexDecodeInstruction(gInstrFormat, insns, &decInsn);
dumpInstruction(pDexFile, pCode, insnIdx, insnWidth, &decInsn);
insns += insnWidth;
insnIdx += insnWidth;
}
free(className);
}
/*
* Dump a "code" struct.
*/
void dumpCode(DexFile* pDexFile, const DexMethod* pDexMethod)
{
const DexCode* pCode = dexGetCode(pDexFile, pDexMethod);
printf(" registers : %d\n", pCode->registersSize);
printf(" ins : %d\n", pCode->insSize);
printf(" outs : %d\n", pCode->outsSize);
printf(" insns size : %d 16-bit code units\n", pCode->insnsSize);
if (gOptions.disassemble)
dumpBytecodes(pDexFile, pDexMethod);
dumpCatches(pDexFile, pCode);
/* both of these are encoded in debug info */
dumpPositions(pDexFile, pCode, pDexMethod);
dumpLocals(pDexFile, pCode, pDexMethod);
}
/*
* Dump a method.
*/
void dumpMethod(DexFile* pDexFile, const DexMethod* pDexMethod, int i)
{
const DexMethodId* pMethodId;
const char* backDescriptor;
const char* name;
char* typeDescriptor;
char* accessStr;
pMethodId = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
name = dexStringById(pDexFile, pMethodId->nameIdx);
typeDescriptor = dexCopyDescriptorFromMethodId(pDexFile, pMethodId);
backDescriptor = dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
accessStr = createAccessFlagStr(pDexMethod->accessFlags,
kAccessForMethod);
printf(" #%d : (in %s)\n", i, backDescriptor);
printf(" name : '%s'\n", name);
printf(" type : '%s'\n", typeDescriptor);
printf(" access : 0x%04x (%s)\n",
pDexMethod->accessFlags, accessStr);
if (pDexMethod->codeOff == 0) {
printf(" code : (none)\n");
} else {
printf(" code -\n");
dumpCode(pDexFile, pDexMethod);
}
if (gOptions.disassemble)
putchar('\n');
free(typeDescriptor);
free(accessStr);
}
/*
* Dump a static (class) field.
*/
void dumpSField(const DexFile* pDexFile, const DexField* pSField, int i)
{
const DexFieldId* pFieldId;
const char* backDescriptor;
const char* name;
const char* typeDescriptor;
char* accessStr;
pFieldId = dexGetFieldId(pDexFile, pSField->fieldIdx);
name = dexStringById(pDexFile, pFieldId->nameIdx);
typeDescriptor = dexStringByTypeIdx(pDexFile, pFieldId->typeIdx);
backDescriptor = dexStringByTypeIdx(pDexFile, pFieldId->classIdx);
accessStr = createAccessFlagStr(pSField->accessFlags, kAccessForField);
printf(" #%d : (in %s)\n", i, backDescriptor);
printf(" name : '%s'\n", name);
printf(" type : '%s'\n", typeDescriptor);
printf(" access : 0x%04x (%s)\n",
pSField->accessFlags, accessStr);
free(accessStr);
}
/*
* Dump an instance field.
*/
void dumpIField(const DexFile* pDexFile, const DexField* pIField, int i)
{
const DexFieldId* pFieldId;
const char* backDescriptor;
const char* name;
const char* typeDescriptor;
char* accessStr;
pFieldId = dexGetFieldId(pDexFile, pIField->fieldIdx);
name = dexStringById(pDexFile, pFieldId->nameIdx);
typeDescriptor = dexStringByTypeIdx(pDexFile, pFieldId->typeIdx);
backDescriptor = dexStringByTypeIdx(pDexFile, pFieldId->classIdx);
accessStr = createAccessFlagStr(pIField->accessFlags, kAccessForField);
printf(" #%d : (in %s)\n", i, backDescriptor);
printf(" name : '%s'\n", name);
printf(" type : '%s'\n", typeDescriptor);
printf(" access : 0x%04x (%s)\n",
pIField->accessFlags, accessStr);
free(accessStr);
}
/*
* Dump the class.
*/
void dumpClass(DexFile* pDexFile, int idx)
{
const DexTypeList* pInterfaces;
const DexClassDef* pClassDef;
DexClassData* pClassData;
const u1* pEncodedData;
const char* fileName;
const char* classDescriptor;
const char* superclassDescriptor;
char* accessStr;
int i;
pClassDef = dexGetClassDef(pDexFile, idx);
printf("Class #%d -\n", idx);
pEncodedData = dexGetClassData(pDexFile, pClassDef);
pClassData = dexReadAndVerifyClassData(&pEncodedData, NULL);
if (pClassData == NULL) {
printf("Trouble reading class data\n");
return;
}
classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
printf(" Class descriptor : '%s'\n", classDescriptor);
accessStr = createAccessFlagStr(pClassDef->accessFlags, kAccessForClass);
printf(" Access flags : 0x%04x (%s)\n",
pClassDef->accessFlags, accessStr);
if (pClassDef->superclassIdx == kDexNoIndex)
superclassDescriptor = "(none)";
else {
superclassDescriptor =
dexStringByTypeIdx(pDexFile, pClassDef->superclassIdx);
printf(" Superclass : '%s'\n", superclassDescriptor);
}
printf(" Interfaces -\n");
pInterfaces = dexGetInterfacesList(pDexFile, pClassDef);
if (pInterfaces != NULL) {
for (i = 0; i < (int) pInterfaces->size; i++)
dumpInterface(pDexFile, dexGetTypeItem(pInterfaces, i), i);
}
printf(" Static fields -\n");
for (i = 0; i < (int) pClassData->header.staticFieldsSize; i++) {
dumpSField(pDexFile, &pClassData->staticFields[i], i);
}
printf(" Instance fields -\n");
for (i = 0; i < (int) pClassData->header.instanceFieldsSize; i++) {
dumpIField(pDexFile, &pClassData->instanceFields[i], i);
}
printf(" Direct methods -\n");
for (i = 0; i < (int) pClassData->header.directMethodsSize; i++) {
dumpMethod(pDexFile, &pClassData->directMethods[i], i);
}
printf(" Virtual methods -\n");
for (i = 0; i < (int) pClassData->header.virtualMethodsSize; i++) {
dumpMethod(pDexFile, &pClassData->virtualMethods[i], i);
}
// TODO: Annotations.
if (pClassDef->sourceFileIdx != kDexNoIndex)
fileName = dexStringById(pDexFile, pClassDef->sourceFileIdx);
else
fileName = "unknown";
printf(" source_file_idx : %d (%s)\n",
pClassDef->sourceFileIdx, fileName);
printf("\n");
free(pClassData);
free(accessStr);
}
/*
* Dump the requested sections of the file.
*/
void processDexFile(const char* fileName, DexFile* pDexFile)
{
int i;
printf("Opened '%s', DEX version '%.3s'\n", fileName,
pDexFile->pHeader->magic +4);
if (gOptions.showFileHeaders)
dumpFileHeader(pDexFile);
for (i = 0; i < (int) pDexFile->pHeader->classDefsSize; i++) {
if (gOptions.showSectionHeaders)
dumpClassDef(pDexFile, i);
dumpClass(pDexFile, i);
}
}
/*
* Process one file.
*/
int process(const char* fileName)
{
DexFile* pDexFile = NULL;
MemMapping map;
bool mapped = false;
int result = -1;
printf("Processing '%s'...\n", fileName);
if (dexOpenAndMap(fileName, gOptions.tempFileName, &map, false) != 0)
goto bail;
mapped = true;
pDexFile = dexFileParse(map.addr, map.length,
kDexParseVerifyChecksum | kDexParseContinueOnError);
if (pDexFile == NULL) {
fprintf(stderr, "ERROR: DEX parse failed\n");
goto bail;
}
processDexFile(fileName, pDexFile);
result = 0;
bail:
if (mapped)
sysReleaseShmem(&map);
if (pDexFile != NULL)
dexFileFree(pDexFile);
return result;
}
/*
* Show usage.
*/
void usage(void)
{
fprintf(stderr, "Copyright (C) 2007 The Android Open Source Project\n\n");
fprintf(stderr, "%s: [-d] [-f] [-h] [-t tempfile] dexfile...\n", gProgName);
fprintf(stderr, "\n");
fprintf(stderr, " -d : disassemble code sections\n");
fprintf(stderr, " -f : display summary information from file header\n");
fprintf(stderr, " -h : display file header details\n");
fprintf(stderr, " -t : temp file name (defaults to /sdcard/dex-temp-*)\n");
}
/*
* Parse args.
*
* I'm not using getopt_long() because we may not have it in libc.
*/
int main(int argc, char* const argv[])
{
bool wantUsage = false;
int ic;
memset(&gOptions, 0, sizeof(gOptions));
while (1) {
ic = getopt(argc, argv, "dfht:");
if (ic < 0)
break;
switch (ic) {
case 'd': // disassemble Dalvik instructions
gOptions.disassemble = true;
break;
case 'f': // dump outer file header
gOptions.showFileHeaders = true;
break;
case 'h': // dump section headers, i.e. all meta-data
gOptions.showSectionHeaders = true;
break;
case 't': // temp file, used when opening compressed Jar
gOptions.tempFileName = argv[optind];
break;
default:
wantUsage = true;
break;
}
}
if (optind == argc) {
fprintf(stderr, "%s: no file specified\n", gProgName);
wantUsage = true;
}
/* initialize some VM tables */
gInstrWidth = dexCreateInstrWidthTable();
gInstrFormat = dexCreateInstrFormatTable();
if (wantUsage) {
usage();
return 2;
}
while (optind < argc)
process(argv[optind++]);
free(gInstrWidth);
free(gInstrFormat);
return 0;
}