summaryrefslogtreecommitdiffstats
path: root/dexdump
diff options
context:
space:
mode:
authorAndy McFadden <fadden@android.com>2009-05-06 10:19:16 -0700
committerAndy McFadden <fadden@android.com>2009-05-06 10:19:16 -0700
commitd18aff3e3ef753a0b6725f1b0a9cd47faa5989e0 (patch)
tree93a5ce9236e90ef788342acdb63e6a03e87f6d5a /dexdump
parent39cf7d7849f7b0c55f749fc538c8b1960c16e5bb (diff)
parenta2ee53bac4db4c77aa2bb31bad8a9d177fd09301 (diff)
downloadandroid_dalvik-d18aff3e3ef753a0b6725f1b0a9cd47faa5989e0.tar.gz
android_dalvik-d18aff3e3ef753a0b6725f1b0a9cd47faa5989e0.tar.bz2
android_dalvik-d18aff3e3ef753a0b6725f1b0a9cd47faa5989e0.zip
merge a2ee53b and resolved conflicts...
Diffstat (limited to 'dexdump')
-rw-r--r--dexdump/DexDump.c529
1 files changed, 443 insertions, 86 deletions
diff --git a/dexdump/DexDump.c b/dexdump/DexDump.c
index bd9123671..6fed7aaf4 100644
--- a/dexdump/DexDump.c
+++ b/dexdump/DexDump.c
@@ -18,7 +18,16 @@
* 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
+ * TODO: rework the "plain" output format to be more regexp-friendly
+ *
+ * Differences between XML output and the "current.xml" file:
+ * - classes in same package are not all grouped together; generally speaking
+ * nothing is sorted
+ * - no "deprecated" on fields and methods
+ * - no "value" on fields
+ * - no parameter names
+ * - no generic signatures on parameters, e.g. type="java.lang.Class&lt;?&gt;"
+ * - class shows declared fields and methods; does not show inherited fields
*/
#include "libdex/DexFile.h"
#include "libdex/DexCatch.h"
@@ -44,6 +53,11 @@ static const char* gProgName = "dexdump";
static InstructionWidth* gInstrWidth;
static InstructionFormat* gInstrFormat;
+typedef enum OutputFormat {
+ OUTPUT_PLAIN = 0, /* default */
+ OUTPUT_XML, /* fancy */
+} OutputFormat;
+
/* command-line options */
struct {
bool checksumOnly;
@@ -52,7 +66,10 @@ struct {
bool showSectionHeaders;
bool ignoreBadChecksum;
bool dumpRegisterMaps;
+ OutputFormat outputFormat;
const char* tempFileName;
+ bool exportsOnly;
+ bool verbose;
} gOptions;
/* basic info about a field or method */
@@ -79,34 +96,134 @@ static inline u4 get4LE(unsigned char const* pSrc)
}
/*
- * 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 '.'.
+ * Converts a single-character primitive type into its human-readable
+ * equivalent.
+ */
+static const char* primitiveTypeLabel(char typeChar)
+{
+ switch (typeChar) {
+ case 'B': return "byte";
+ case 'C': return "char";
+ case 'D': return "double";
+ case 'F': return "float";
+ case 'I': return "int";
+ case 'J': return "long";
+ case 'S': return "short";
+ case 'V': return "void";
+ case 'Z': return "boolean";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+/*
+ * Converts a type descriptor to human-readable "dotted" form. For
+ * example, "Ljava/lang/String;" becomes "java.lang.String", and
+ * "[I" becomes "int[]". Also converts '$' to '.', which means this
+ * form can't be converted back to a descriptor.
*/
static char* descriptorToDot(const char* str)
{
- size_t at = strlen(str);
+ int targetLen = strlen(str);
+ int offset = 0;
+ int arrayDepth = 0;
char* newStr;
- if (str[0] == 'L') {
- assert(str[at - 1] == ';');
- at -= 2; /* Two fewer chars to copy. */
- str++; /* Skip the 'L'. */
+ /* strip leading [s; will be added to end */
+ while (targetLen > 1 && str[offset] == '[') {
+ offset++;
+ targetLen--;
}
+ arrayDepth = offset;
- newStr = malloc(at + 1); /* Add one for the '\0'. */
- newStr[at] = '\0';
+ if (targetLen == 1) {
+ /* primitive type */
+ str = primitiveTypeLabel(str[offset]);
+ offset = 0;
+ targetLen = strlen(str);
+ } else {
+ /* account for leading 'L' and trailing ';' */
+ if (targetLen >= 2 && str[offset] == 'L' &&
+ str[offset+targetLen-1] == ';')
+ {
+ targetLen -= 2;
+ offset++;
+ }
+ }
+
+ newStr = malloc(targetLen + arrayDepth * 2 +1);
- while (at > 0) {
- at--;
- newStr[at] = (str[at] == '/') ? '.' : str[at];
+ /* copy class name over */
+ int i;
+ for (i = 0; i < targetLen; i++) {
+ char ch = str[offset + i];
+ newStr[i] = (ch == '/' || ch == '$') ? '.' : ch;
+ }
+
+ /* add the appropriate number of brackets for arrays */
+ while (arrayDepth-- > 0) {
+ newStr[i++] = '[';
+ newStr[i++] = ']';
}
+ newStr[i] = '\0';
+ assert(i == targetLen + arrayDepth * 2);
return newStr;
}
/*
+ * Converts the class name portion of a type descriptor to human-readable
+ * "dotted" form.
+ *
+ * Returns a newly-allocated string.
+ */
+static char* descriptorClassToDot(const char* str)
+{
+ const char* lastSlash;
+ char* newStr;
+ char* cp;
+
+ /* reduce to just the class name, trimming trailing ';' */
+ lastSlash = strrchr(str, '/');
+ if (lastSlash == NULL)
+ lastSlash = str + 1; /* start past 'L' */
+ else
+ lastSlash++; /* start past '/' */
+
+ newStr = strdup(lastSlash);
+ newStr[strlen(lastSlash)-1] = '\0';
+ for (cp = newStr; *cp != '\0'; cp++) {
+ if (*cp == '$')
+ *cp = '.';
+ }
+
+ return newStr;
+}
+
+/*
+ * Returns a quoted string representing the boolean value.
+ */
+static const char* quotedBool(bool val)
+{
+ if (val)
+ return "\"true\"";
+ else
+ return "\"false\"";
+}
+
+static const char* quotedVisibility(u4 accessFlags)
+{
+ if ((accessFlags & ACC_PUBLIC) != 0)
+ return "\"public\"";
+ else if ((accessFlags & ACC_PROTECTED) != 0)
+ return "\"protected\"";
+ else if ((accessFlags & ACC_PRIVATE) != 0)
+ return "\"private\"";
+ else
+ return "\"package\"";
+}
+
+/*
* Count the number of '1' bits in a word.
*
* Having completed this, I'm ready for an interview at Google.
@@ -126,7 +243,6 @@ static int countOnes(u4 val)
return count;
}
-
/*
* Flag for use with createAccessFlagStr().
*/
@@ -322,7 +438,7 @@ void dumpClassDef(DexFile* pDexFile, int idx)
}
/*
- * Dump an interface.
+ * Dump an interface that a class declares to implement.
*/
void dumpInterface(const DexFile* pDexFile, const DexTypeItem* pTypeItem,
int i)
@@ -330,7 +446,13 @@ void dumpInterface(const DexFile* pDexFile, const DexTypeItem* pTypeItem,
const char* interfaceName =
dexStringByTypeIdx(pDexFile, pTypeItem->typeIdx);
- printf(" #%d : '%s'\n", i, interfaceName);
+ if (gOptions.outputFormat == OUTPUT_PLAIN) {
+ printf(" #%d : '%s'\n", i, interfaceName);
+ } else {
+ char* dotted = descriptorToDot(interfaceName);
+ printf("<implements name=\"%s\">\n</implements>\n", dotted);
+ free(dotted);
+ }
}
/*
@@ -878,8 +1000,14 @@ void dumpMethod(DexFile* pDexFile, const DexMethod* pDexMethod, int i)
const DexMethodId* pMethodId;
const char* backDescriptor;
const char* name;
- char* typeDescriptor;
- char* accessStr;
+ char* typeDescriptor = NULL;
+ char* accessStr = NULL;
+
+ if (gOptions.exportsOnly &&
+ (pDexMethod->accessFlags & (ACC_PUBLIC | ACC_PROTECTED)) == 0)
+ {
+ return;
+ }
pMethodId = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
name = dexStringById(pDexFile, pMethodId->nameIdx);
@@ -890,22 +1018,119 @@ void dumpMethod(DexFile* pDexFile, const DexMethod* pDexMethod, int i)
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 (gOptions.outputFormat == OUTPUT_PLAIN) {
+ 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 (pDexMethod->codeOff == 0) {
+ printf(" code : (none)\n");
+ } else {
+ printf(" code -\n");
+ dumpCode(pDexFile, pDexMethod);
+ }
- if (gOptions.disassemble)
- putchar('\n');
+ if (gOptions.disassemble)
+ putchar('\n');
+ } else if (gOptions.outputFormat == OUTPUT_XML) {
+ bool constructor = (name[0] == '<');
+
+ if (constructor) {
+ char* tmp;
+
+ tmp = descriptorClassToDot(backDescriptor);
+ printf("<constructor name=\"%s\"\n", tmp);
+ free(tmp);
+
+ tmp = descriptorToDot(backDescriptor);
+ printf(" type=\"%s\"\n", tmp);
+ free(tmp);
+ } else {
+ printf("<method name=\"%s\"\n", name);
+
+ const char* returnType = strrchr(typeDescriptor, ')');
+ if (returnType == NULL) {
+ fprintf(stderr, "bad method type descriptor '%s'\n",
+ typeDescriptor);
+ goto bail;
+ }
+
+ char* tmp = descriptorToDot(returnType+1);
+ printf(" return=\"%s\"\n", tmp);
+ free(tmp);
+
+ printf(" abstract=%s\n",
+ quotedBool((pDexMethod->accessFlags & ACC_ABSTRACT) != 0));
+ printf(" native=%s\n",
+ quotedBool((pDexMethod->accessFlags & ACC_NATIVE) != 0));
+
+ bool isSync =
+ (pDexMethod->accessFlags & ACC_SYNCHRONIZED) != 0 ||
+ (pDexMethod->accessFlags & ACC_DECLARED_SYNCHRONIZED) != 0;
+ printf(" synchronized=%s\n", quotedBool(isSync));
+ }
+
+ printf(" static=%s\n",
+ quotedBool((pDexMethod->accessFlags & ACC_STATIC) != 0));
+ printf(" final=%s\n",
+ quotedBool((pDexMethod->accessFlags & ACC_FINAL) != 0));
+ // "deprecated=" not knowable w/o parsing annotations
+ printf(" visibility=%s\n",
+ quotedVisibility(pDexMethod->accessFlags));
+
+ printf(">\n");
+
+ /*
+ * Parameters.
+ */
+ if (typeDescriptor[0] != '(') {
+ fprintf(stderr, "ERROR: bad descriptor '%s'\n", typeDescriptor);
+ goto bail;
+ }
+
+ char tmpBuf[strlen(typeDescriptor)+1]; /* more than big enough */
+ int argNum = 0;
+ const char* base = typeDescriptor+1;
+
+ while (*base != ')') {
+ char* cp = tmpBuf;
+
+ while (*base == '[')
+ *cp++ = *base++;
+
+ if (*base == 'L') {
+ /* copy through ';' */
+ do {
+ *cp = *base++;
+ } while (*cp++ != ';');
+ } else {
+ /* primitive char, copy it */
+ if (strchr("ZBCSIFJD", *base) == NULL) {
+ fprintf(stderr, "ERROR: bad method signature '%s'\n", base);
+ goto bail;
+ }
+ *cp++ = *base++;
+ }
+
+ /* null terminate and display */
+ *cp++ = '\0';
+
+ char* tmp = descriptorToDot(tmpBuf);
+ printf("<parameter name=\"arg%d\" type=\"%s\">\n</parameter>\n",
+ argNum++, tmp);
+ free(tmp);
+ }
+
+ if (constructor)
+ printf("</constructor>\n");
+ else
+ printf("</method>\n");
+ }
+
+bail:
free(typeDescriptor);
free(accessStr);
}
@@ -921,6 +1146,12 @@ void dumpSField(const DexFile* pDexFile, const DexField* pSField, int i)
const char* typeDescriptor;
char* accessStr;
+ if (gOptions.exportsOnly &&
+ (pSField->accessFlags & (ACC_PUBLIC | ACC_PROTECTED)) == 0)
+ {
+ return;
+ }
+
pFieldId = dexGetFieldId(pDexFile, pSField->fieldIdx);
name = dexStringById(pDexFile, pFieldId->nameIdx);
typeDescriptor = dexStringByTypeIdx(pDexFile, pFieldId->typeIdx);
@@ -928,11 +1159,35 @@ void dumpSField(const DexFile* pDexFile, const DexField* pSField, int i)
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);
+ if (gOptions.outputFormat == OUTPUT_PLAIN) {
+ 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);
+ } else if (gOptions.outputFormat == OUTPUT_XML) {
+ char* tmp;
+
+ printf("<field name=\"%s\"\n", name);
+
+ tmp = descriptorToDot(typeDescriptor);
+ printf(" type=\"%s\"\n", tmp);
+ free(tmp);
+
+ printf(" transient=%s\n",
+ quotedBool((pSField->accessFlags & ACC_TRANSIENT) != 0));
+ printf(" volatile=%s\n",
+ quotedBool((pSField->accessFlags & ACC_VOLATILE) != 0));
+ // "value=" not knowable w/o parsing annotations
+ printf(" static=%s\n",
+ quotedBool((pSField->accessFlags & ACC_STATIC) != 0));
+ printf(" final=%s\n",
+ quotedBool((pSField->accessFlags & ACC_FINAL) != 0));
+ // "deprecated=" not knowable w/o parsing annotations
+ printf(" visibility=%s\n",
+ quotedVisibility(pSField->accessFlags));
+ printf(">\n</field>\n");
+ }
free(accessStr);
}
@@ -942,94 +1197,160 @@ void dumpSField(const DexFile* pDexFile, const DexField* pSField, int i)
*/
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);
+ dumpSField(pDexFile, pIField, i);
}
/*
* Dump the class.
*
* Note "idx" is a DexClassDef index, not a DexTypeId index.
+ *
+ * If "*pLastPackage" is NULL or does not match the current class' package,
+ * the value will be replaced with a newly-allocated string.
*/
-void dumpClass(DexFile* pDexFile, int idx)
+void dumpClass(DexFile* pDexFile, int idx, char** pLastPackage)
{
const DexTypeList* pInterfaces;
const DexClassDef* pClassDef;
- DexClassData* pClassData;
+ DexClassData* pClassData = NULL;
const u1* pEncodedData;
const char* fileName;
const char* classDescriptor;
const char* superclassDescriptor;
- char* accessStr;
+ char* accessStr = NULL;
int i;
pClassDef = dexGetClassDef(pDexFile, idx);
- printf("Class #%d -\n", idx);
+
+ if (gOptions.exportsOnly && (pClassDef->accessFlags & ACC_PUBLIC) == 0) {
+ //printf("<!-- omitting non-public class %s -->\n",
+ // classDescriptor);
+ goto bail;
+ }
pEncodedData = dexGetClassData(pDexFile, pClassDef);
pClassData = dexReadAndVerifyClassData(&pEncodedData, NULL);
if (pClassData == NULL) {
- printf("Trouble reading class data\n");
- return;
+ printf("Trouble reading class data (#%d)\n", idx);
+ goto bail;
}
classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
- printf(" Class descriptor : '%s'\n", classDescriptor);
+
+ /*
+ * For the XML output, show the package name. Ideally we'd gather
+ * up the classes, sort them, and dump them alphabetically so the
+ * package name wouldn't jump around, but that's not a great plan
+ * for something that needs to run on the device.
+ */
+ if (!(classDescriptor[0] == 'L' &&
+ classDescriptor[strlen(classDescriptor)-1] == ';'))
+ {
+ /* arrays and primitives should not be defined explicitly */
+ fprintf(stderr, "Malformed class name '%s'\n", classDescriptor);
+ /* keep going? */
+ } else if (gOptions.outputFormat == OUTPUT_XML) {
+ char* mangle;
+ char* lastSlash;
+ char* cp;
+
+ mangle = strdup(classDescriptor + 1);
+ mangle[strlen(mangle)-1] = '\0';
+
+ /* reduce to just the package name */
+ lastSlash = strrchr(mangle, '/');
+ if (lastSlash != NULL) {
+ *lastSlash = '\0';
+ } else {
+ *mangle = '\0';
+ }
+
+ for (cp = mangle; *cp != '\0'; cp++) {
+ if (*cp == '/')
+ *cp = '.';
+ }
+
+ if (*pLastPackage == NULL || strcmp(mangle, *pLastPackage) != 0) {
+ /* start of a new package */
+ if (*pLastPackage != NULL)
+ printf("</package>\n");
+ printf("<package name=\"%s\"\n>\n", mangle);
+ free(*pLastPackage);
+ *pLastPackage = mangle;
+ } else {
+ free(mangle);
+ }
+ }
accessStr = createAccessFlagStr(pClassDef->accessFlags, kAccessForClass);
- printf(" Access flags : 0x%04x (%s)\n",
- pClassDef->accessFlags, accessStr);
- if (pClassDef->superclassIdx == kDexNoIndex)
- superclassDescriptor = "(none)";
- else {
+ if (pClassDef->superclassIdx == kDexNoIndex) {
+ superclassDescriptor = NULL;
+ } else {
superclassDescriptor =
dexStringByTypeIdx(pDexFile, pClassDef->superclassIdx);
- printf(" Superclass : '%s'\n", superclassDescriptor);
}
- printf(" Interfaces -\n");
+ if (gOptions.outputFormat == OUTPUT_PLAIN) {
+ printf("Class #%d -\n", idx);
+ printf(" Class descriptor : '%s'\n", classDescriptor);
+ printf(" Access flags : 0x%04x (%s)\n",
+ pClassDef->accessFlags, accessStr);
+
+ if (superclassDescriptor != NULL)
+ printf(" Superclass : '%s'\n", superclassDescriptor);
+
+ printf(" Interfaces -\n");
+ } else {
+ char* tmp;
+
+ tmp = descriptorClassToDot(classDescriptor);
+ printf("<class name=\"%s\"\n", tmp);
+ free(tmp);
+
+ if (superclassDescriptor != NULL) {
+ tmp = descriptorToDot(superclassDescriptor);
+ printf(" extends=\"%s\"\n", tmp);
+ free(tmp);
+ }
+ printf(" abstract=%s\n",
+ quotedBool((pClassDef->accessFlags & ACC_ABSTRACT) != 0));
+ printf(" static=%s\n",
+ quotedBool((pClassDef->accessFlags & ACC_STATIC) != 0));
+ printf(" final=%s\n",
+ quotedBool((pClassDef->accessFlags & ACC_FINAL) != 0));
+ // "deprecated=" not knowable w/o parsing annotations
+ printf(" visibility=%s\n",
+ quotedVisibility(pClassDef->accessFlags));
+ printf(">\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");
+ if (gOptions.outputFormat == OUTPUT_PLAIN)
+ printf(" Static fields -\n");
for (i = 0; i < (int) pClassData->header.staticFieldsSize; i++) {
dumpSField(pDexFile, &pClassData->staticFields[i], i);
}
- printf(" Instance fields -\n");
+ if (gOptions.outputFormat == OUTPUT_PLAIN)
+ printf(" Instance fields -\n");
for (i = 0; i < (int) pClassData->header.instanceFieldsSize; i++) {
dumpIField(pDexFile, &pClassData->instanceFields[i], i);
}
- printf(" Direct methods -\n");
+ if (gOptions.outputFormat == OUTPUT_PLAIN)
+ printf(" Direct methods -\n");
for (i = 0; i < (int) pClassData->header.directMethodsSize; i++) {
dumpMethod(pDexFile, &pClassData->directMethods[i], i);
}
- printf(" Virtual methods -\n");
+ if (gOptions.outputFormat == OUTPUT_PLAIN)
+ printf(" Virtual methods -\n");
for (i = 0; i < (int) pClassData->header.virtualMethodsSize; i++) {
dumpMethod(pDexFile, &pClassData->virtualMethods[i], i);
}
@@ -1040,11 +1361,18 @@ void dumpClass(DexFile* pDexFile, int idx)
fileName = dexStringById(pDexFile, pClassDef->sourceFileIdx);
else
fileName = "unknown";
- printf(" source_file_idx : %d (%s)\n",
- pClassDef->sourceFileIdx, fileName);
- printf("\n");
+ if (gOptions.outputFormat == OUTPUT_PLAIN) {
+ printf(" source_file_idx : %d (%s)\n",
+ pClassDef->sourceFileIdx, fileName);
+ printf("\n");
+ }
+
+ if (gOptions.outputFormat == OUTPUT_XML) {
+ printf("</class>\n");
+ }
+bail:
free(pClassData);
free(accessStr);
}
@@ -1256,10 +1584,13 @@ void dumpRegisterMaps(DexFile* pDexFile)
*/
void processDexFile(const char* fileName, DexFile* pDexFile)
{
+ char* package = NULL;
int i;
- printf("Opened '%s', DEX version '%.3s'\n", fileName,
- pDexFile->pHeader->magic +4);
+ if (gOptions.verbose) {
+ printf("Opened '%s', DEX version '%.3s'\n", fileName,
+ pDexFile->pHeader->magic +4);
+ }
if (gOptions.dumpRegisterMaps) {
dumpRegisterMaps(pDexFile);
@@ -1269,12 +1600,24 @@ void processDexFile(const char* fileName, DexFile* pDexFile)
if (gOptions.showFileHeaders)
dumpFileHeader(pDexFile);
+ if (gOptions.outputFormat == OUTPUT_XML)
+ printf("<api>\n");
+
for (i = 0; i < (int) pDexFile->pHeader->classDefsSize; i++) {
if (gOptions.showSectionHeaders)
dumpClassDef(pDexFile, i);
- dumpClass(pDexFile, i);
+ dumpClass(pDexFile, i, &package);
+ }
+
+ /* free the last one allocated */
+ if (package != NULL) {
+ printf("</package>\n");
+ free(package);
}
+
+ if (gOptions.outputFormat == OUTPUT_XML)
+ printf("</api>\n");
}
@@ -1288,7 +1631,8 @@ int process(const char* fileName)
bool mapped = false;
int result = -1;
- printf("Processing '%s'...\n", fileName);
+ if (gOptions.verbose)
+ printf("Processing '%s'...\n", fileName);
if (dexOpenAndMap(fileName, gOptions.tempFileName, &map, false) != 0)
goto bail;
@@ -1328,7 +1672,7 @@ void usage(void)
{
fprintf(stderr, "Copyright (C) 2007 The Android Open Source Project\n\n");
fprintf(stderr,
- "%s: [-c] [-d] [-f] [-h] [-m] [-i] [-t tempfile] dexfile...\n",
+ "%s: [-c] [-d] [-f] [-h] [-i] [-l layout] [-m] [-t tempfile] dexfile...\n",
gProgName);
fprintf(stderr, "\n");
fprintf(stderr, " -c : verify checksum and exit\n");
@@ -1336,6 +1680,7 @@ void usage(void)
fprintf(stderr, " -f : display summary information from file header\n");
fprintf(stderr, " -h : display file header details\n");
fprintf(stderr, " -i : ignore checksum failures\n");
+ fprintf(stderr, " -l : output layout, either 'plain' or 'xml'\n");
fprintf(stderr, " -m : dump register maps (and nothing else)\n");
fprintf(stderr, " -t : temp file name (defaults to /sdcard/dex-temp-*)\n");
}
@@ -1351,9 +1696,10 @@ int main(int argc, char* const argv[])
int ic;
memset(&gOptions, 0, sizeof(gOptions));
+ gOptions.verbose = true;
while (1) {
- ic = getopt(argc, argv, "cdfhimt:");
+ ic = getopt(argc, argv, "cdfhil:mt:");
if (ic < 0)
break;
@@ -1373,11 +1719,22 @@ int main(int argc, char* const argv[])
case 'i': // continue even if checksum is bad
gOptions.ignoreBadChecksum = true;
break;
+ case 'l': // layout
+ if (strcmp(optarg, "plain") == 0) {
+ gOptions.outputFormat = OUTPUT_PLAIN;
+ } else if (strcmp(optarg, "xml") == 0) {
+ gOptions.outputFormat = OUTPUT_XML;
+ gOptions.verbose = false;
+ gOptions.exportsOnly = true;
+ } else {
+ wantUsage = true;
+ }
+ break;
case 'm': // dump register maps only
gOptions.dumpRegisterMaps = true;
break;
case 't': // temp file, used when opening compressed Jar
- gOptions.tempFileName = argv[optind];
+ gOptions.tempFileName = optarg;
break;
default:
wantUsage = true;